> This page is for Gateway.

> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.merge.dev/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.merge.dev/_mcp/server.

# Errors

> The Gateway error envelope and every customer-facing error code, grouped by HTTP status, with whether to retry and what to do. Also covers failover and errors mid-stream.

Gateway errors carry a stable `code` to branch on and a `source` saying whether Gateway or the upstream vendor failed. Under a routing policy, Gateway already retries some failures on another route, so read [what fails over](#what-fails-over) before adding client retries.

## The error envelope

Model endpoints (`/v1/responses`, the SDK-compatible surfaces, embeddings, media, and batches) return:

```json
{
  "error": {
    "type": "not_found_error",
    "message": "Model 'openai/gpt-9' is not supported. Check /v1/models for available models.",
    "code": "model_not_found",
    "param": "model",
    "source": "gateway",
    "provider": null
  }
}
```

| Field      | Meaning                                                                                                                                                                                                                                                                                           |
| ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `type`     | Broad category: `invalid_request_error`, `authentication_error`, `invalid_api_key`, `permission_error`, `blocked_by_policy`, `blocked_by_pi_protection`, `not_found_error`, `budget_exceeded`, `payment_failed`, `rate_limit_error`, `provider_error`, `internal_error`, or `service_unavailable` |
| `code`     | The specific identifier. Branch on it, not on `message`. Some errors have no `code`, so check `type` too.                                                                                                                                                                                         |
| `param`    | The request field at fault, when there is one                                                                                                                                                                                                                                                     |
| `source`   | `gateway` when Gateway rejected the request, `provider` when the upstream vendor failed                                                                                                                                                                                                           |
| `provider` | The model's author (for example `openai`) when `source` is `provider`                                                                                                                                                                                                                             |

Rejections before Gateway reads the body (authentication, budgets, rate limits, blocklist rules, model access, prompt injection, and DLP) send only `type`, `message`, and usually `code`, so read `param`, `source`, and `provider` defensively. Schema validation failures on native `POST /v1/responses` return `422` with a `detail` array instead of `error`.

The **Retry** column says whether resending the same request can succeed: "Yes" means back off and resend, "No" means change the request or your configuration first.

## Invalid requests: 400

**Model and routing**

| `code`                                                                | Meaning                                                                                                                                                                               | Retry | What to do                                                                                   |
| --------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----- | -------------------------------------------------------------------------------------------- |
| `model_required`                                                      | No `model`, and no routing policy resolved (or the resolved policy has no models)                                                                                                     | No    | Send `model`, or set a default routing policy                                                |
| `invalid_model_format`                                                | The model string matched nothing                                                                                                                                                      | No    | Use `provider/model` from `GET /v1/models`                                                   |
| `ambiguous_base_model`                                                | A bare model name matched several providers                                                                                                                                           | No    | Send the qualified `provider/model`                                                          |
| `model_unavailable`                                                   | Every model in the routing policy is inactive                                                                                                                                         | No    | Update the policy's models                                                                   |
| `unsupported_endpoint_for_model`                                      | The model doesn't serve chat, such as an image or video model on a chat endpoint                                                                                                      | No    | Call the endpoint named in the message                                                       |
| `alias_not_supported`                                                 | An `@alias/` model was sent to embeddings, images, audio, video, decisions, or live sessions                                                                                          | No    | [Aliases](/merge-gateway/routing/model-aliases) work on chat surfaces only. Send a model ID. |
| `alias_routing_policy_conflict`                                       | An `@alias/` model and `routing_policy_id` in the same request                                                                                                                        | No    | Send one or the other                                                                        |
| `routing_policy_mismatch`                                             | Body `routing_policy_id` and the `X-Merge-Routing-Policy-Id` header disagree                                                                                                          | No    | Send one value                                                                               |
| `routing_policy_requires_customer`, `routing_policy_requires_project` | The policy is scoped to a customer or project the request doesn't resolve to                                                                                                          | No    | Send the matching `customer` or use a key for that project                                   |
| `self_hosted_unsupported_strategy`                                    | A fallback policy resolves only to self-hosted models                                                                                                                                 | No    | Use a cost-based strategy for self-hosted models                                             |
| `invalid_priority_order`                                              | `priority_order` is empty, has more than 10 entries, has an entry with no model or empty `vendors`, or is combined with `routing_policy_id`, `vendor`, or `vendors`                   | No    | Fix the list. Native `/v1/responses` returns `422` for these.                                |
| `priority_order_model_conflict`                                       | `model` isn't the first `priority_order` entry                                                                                                                                        | No    | Omit `model` or match the first entry. Native returns `422`.                                 |
| `priority_order_routing_policy_conflict`                              | `priority_order` sent with a routing policy id in the body or header                                                                                                                  | No    | Send one or the other                                                                        |
| `vendor_unavailable`                                                  | The `vendor` or `vendors` pin can't serve the model                                                                                                                                   | No    | Check the model's vendors in `GET /v1/models/{model}`                                        |
| `capability_unavailable`                                              | No vendor for the model supports the requested capabilities                                                                                                                           | No    | Drop the capability or pick another model                                                    |
| `vendor_restrictions_unavailable`                                     | No vendor survives your vendor allow and deny lists or [region restrictions](/merge-gateway/security/geo-location-routing)                                                            | No    | Loosen the restriction or pick another model                                                 |
| `zdr_vendor_unavailable`                                              | Zero data retention is on and no ZDR vendor serves the model                                                                                                                          | No    | Pick a model with a ZDR vendor                                                               |
| `unsupported_params`                                                  | The route can't serve part of the request, such as tool calling, [structured outputs](/merge-gateway/capabilities/structured-outputs), an input type, or the requested `service_tier` | No    | Remove the parameter or route elsewhere                                                      |

**Scope**

| `code`                                          | Meaning                                                                    | Retry | What to do                                |
| ----------------------------------------------- | -------------------------------------------------------------------------- | ----- | ----------------------------------------- |
| `project_mismatch`                              | The request's project conflicts with a project-scoped key                  | No    | Drop `project_id` or use the matching key |
| `customer_mismatch`                             | `customer` conflicts with the header or with a customer API key's customer | No    | Send one customer                         |
| `customer_project_conflict`                     | A request combines a customer with any project scope                       | No    | Send customer traffic without a project   |
| `invalid_customer`, `invalid_routing_policy_id` | `X-Merge-Customer` or `X-Merge-Routing-Policy-Id` isn't a UUID             | No    | Send the UUID                             |

**Request body**

| `code`                                                         | Meaning                                                                                                                                                          | Retry | What to do                                             |
| -------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----- | ------------------------------------------------------ |
| `empty_input`, `empty_messages`                                | `input` or `messages` is empty                                                                                                                                   | No    | Send at least one item                                 |
| `invalid_input_item`                                           | A Responses input message has no valid role                                                                                                                      | No    | Use `system`, `developer`, `user`, or `assistant`      |
| `invalid_message_content`, `invalid_tool_messages`             | Message content or the tool call and tool result sequence is malformed                                                                                           | No    | Fix the message the error names                        |
| `invalid_openai_responses_request`                             | The OpenAI Responses payload isn't an object                                                                                                                     | No    | Send a JSON object body                                |
| `invalid_request_body`, `malformed_request_body`               | The body can't be parsed, or a gzip body is corrupt                                                                                                              | No    | Fix the body or its `Content-Encoding`                 |
| `invalid_response_format`                                      | `response_format` or `text.format` is malformed, such as `json_schema` without a schema                                                                          | No    | Fix the format object                                  |
| `invalid_thinking_budget`                                      | `thinking.budget_tokens` isn't a positive integer (SDK-compatible surfaces)                                                                                      | No    | Send a positive integer                                |
| `unsupported_reasoning_effort`                                 | The effort isn't one of `none`, `minimal`, `low`, `medium`, `high`, `xhigh`, or `max`. A known level the route doesn't accept is clamped with a warning instead. | No    | Send a known level                                     |
| `output_budget_exhausted`                                      | The model spent its whole output budget on reasoning and returned nothing                                                                                        | No    | Raise the output token limit or lower reasoning effort |
| `unknown_tags`                                                 | A tag key-value pair isn't defined for your organization                                                                                                         | No    | List available pairs with `GET /v1/tags`               |
| `previous_response_id_unsupported`, `conversation_unsupported` | `conversation` was sent, or `previous_response_id` can't be resumed for your organization                                                                        | No    | Send the full conversation in `input`                  |
| `invalid_previous_response_id`                                 | `previous_response_id` isn't a string                                                                                                                            | No    | Send the `resp_` id                                    |
| `input_too_long`                                               | Speech input is over the vendor's character limit                                                                                                                | No    | Shorten or split the text                              |
| `invalid_date`                                                 | A usage endpoint date isn't `YYYY-MM-DD`                                                                                                                         | No    | Fix the date                                           |

**Tools and web search**

| `code`                                                                    | Meaning                                                                                                                                     | Retry | What to do                                             |
| ------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- | ----- | ------------------------------------------------------ |
| `invalid_tool_definition`, `invalid_tool_name`, `invalid_tool_parameters` | A tool is malformed, has an empty name, or `parameters` isn't a JSON Schema object                                                          | No    | Fix the tool                                           |
| `unsupported_tool_type`                                                   | A tool type the route can't execute, such as `code_interpreter`, `file_search`, `mcp`, `local_shell`, or `custom` on native `/v1/responses` | No    | Remove the tool                                        |
| `unsupported_provider_native_tool`                                        | An Anthropic server tool such as `web_search_20250305` on a non-Anthropic route                                                             | No    | Use `merge:web_search`, or route to Anthropic          |
| `openai_tool_search_requires_openai`, `openai_web_search_requires_openai` | An OpenAI hosted tool on a request that can't reach OpenAI directly                                                                         | No    | Route to an `openai/` model on the `openai` vendor     |
| `web_search_requires_provider`                                            | A vendor-hosted search tool on a route whose vendor can't run it, under the `native` web search mode                                        | No    | Use `merge:web_search`, or route to the hosting vendor |
| `invalid_web_search_mode`                                                 | `provider_options.web_search` isn't `auto`, `native`, or `merge`                                                                            | No    | Send a valid mode                                      |
| `web_search_unavailable`                                                  | The chosen [web search](/merge-gateway/capabilities/web-search) engine has no key configured                                                | No    | Pass the engine's `api_key` or pick another engine     |
| `unsupported_web_search_engine`                                           | Unknown `engine`                                                                                                                            | No    | Use an engine the message lists                        |
| `invalid_server_tool_parameters`, `invalid_search_context_size`           | A web search parameter is out of range, such as `max_results` above 25                                                                      | No    | Fix the parameter                                      |
| `duplicate_server_tool`                                                   | More than one web search tool                                                                                                               | No    | Send one                                               |
| `server_tool_zdr_unavailable`                                             | The engine isn't ZDR-compliant and zero data retention is on                                                                                | No    | Use an engine the message lists                        |
| `web_search_output_budget_exhausted`                                      | OpenAI hosted search ran out of output tokens before answering                                                                              | No    | Raise `max_output_tokens` or leave it unset            |

**Fusion and images**

| `code`                                                                                                                 | Meaning                                                                                                                                                                 | Retry | What to do                             |
| ---------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----- | -------------------------------------- |
| `fusion_config_required`, `fusion_stream_unsupported`, `fusion_unsupported_tools`, `fusion_routing_policy_unsupported` | A [Fusion](/merge-gateway/capabilities/fusion) constraint: no config, streaming, unsupported tools, or a routing policy                                                 | No    | Adjust the request as the message says |
| `image_input_unsupported`                                                                                              | The model on `/v1/images/edits` doesn't accept image input                                                                                                              | No    | Pick a model with `image` input        |
| `image_required`, `image_empty`, `too_many_images`, `image_too_large`, `uploads_too_large`, `stream_unsupported`       | An [image edit](/merge-gateway/capabilities/multimodal/image-editing) upload is missing, empty, more than 16 images, over 50 MB each or 100 MB total, or `stream: true` | No    | Fix the upload                         |

**Vendor-reported**

| `code`                     | Meaning                                                                                                           | Retry | What to do                                                         |
| -------------------------- | ----------------------------------------------------------------------------------------------------------------- | ----- | ------------------------------------------------------------------ |
| `context_window_exceeded`  | The vendor rejected the request as too long (`source: provider`)                                                  | No    | Shorten the input or pick a larger context window                  |
| `content_policy_violation` | The vendor's content policy blocked it (`source: provider`)                                                       | No    | Change the content. A routing policy fails over to another author. |
| `unsupported_parameter`    | The vendor doesn't accept a parameter (`source: provider`). Batches also use it for an unknown `provider` option. | No    | Remove the parameter                                               |
| The vendor's own code      | Any other vendor `400`, with `type: provider_error` and `source: provider`                                        | No    | Read `message`                                                     |

## Authentication: 401

| `type` (no `code`)      | Meaning                                                                                               | Retry | What to do                                       |
| ----------------------- | ----------------------------------------------------------------------------------------------------- | ----- | ------------------------------------------------ |
| `invalid_request_error` | Missing key, or an `Authorization` header that isn't `Bearer <key>`                                   | No    | Send the key                                     |
| `invalid_api_key`       | Unknown, expired, or inactive key; a management key on a model endpoint; or a model key on `/v1/keys` | No    | Use an active key of the right kind              |
| `authentication_error`  | No credential exists for any vendor in the route, such as a BYOK-only setup with no key               | No    | Add the vendor key in your organization settings |

## Payment and budgets: 402

All have `type: budget_exceeded` except `payment_failed`. Gateway checks them before calling any vendor.

| `code`                    | Meaning                                                                                      | Retry       | What to do                                             |
| ------------------------- | -------------------------------------------------------------------------------------------- | ----------- | ------------------------------------------------------ |
| `budget_exceeded`         | Organization credit is used up                                                               | No          | Add credits or upgrade                                 |
| `payment_failed`          | Service is suspended for a failed payment                                                    | No          | Update the payment method                              |
| `project_budget_exceeded` | The project hit its hard limit                                                               | No          | Raise the budget. Changes take up to an hour to apply. |
| `api_key_limit_exceeded`  | The key hit its spend `limit`                                                                | After reset | Raise `limit` or wait for the `limit_reset` window     |
| (none)                    | An [Embedded Routing](/merge-gateway/embedded-routing-stack/budgets) customer hit its budget | After reset | Raise the customer's budget                            |

Every response carries the budget headers for limits that exist: `X-Credit-Balance-USD`, `X-Budget-Limit-USD`, `X-Project-Budget-Remaining-USD`, `X-Project-Budget-Limit-USD`, `X-Key-Limit-Remaining-USD`, and `X-Key-Limit-USD`. `X-Budget-Warning`, `X-Project-Budget-Warning`, and `X-Key-Limit-Warning` appear near a limit. Customer API keys don't get organization or project headers.

## Blocked by policy: 403

| `code`                                                                                               | Meaning                                                                       | Retry | What to do                                                           |
| ---------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------- | ----- | -------------------------------------------------------------------- |
| `customer_blocked`, `customer_pinned`, `customer_model_blocked`, `provider_blocked`, `model_blocked` | A [blocklist rule](/merge-gateway/security/per-customer-restrictions) matched | No    | Change the rule or the model                                         |
| `vendor_access_required`                                                                             | The model's author isn't enabled for your organization                        | No    | Request [provider access](/merge-gateway/model-access/vendor-access) |
| `model_not_allowed`                                                                                  | The key's `allowed_models` excludes the model                                 | No    | Use an allowed model or update the key                               |
| `free_tier_model_not_allowed`                                                                        | The model isn't on the Free plan's list                                       | No    | Add a payment method                                                 |
| (none), `permission_error`                                                                           | The customer is deactivated or deleted                                        | No    | Reactivate the customer                                              |

A block on the requested model has `type: blocked_by_policy`. When every model in a routing policy is blocked, the same codes come with `type: permission_error`.

## Not found: 404

| `code`                                              | Meaning                                                                                                                    | Retry | What to do                          |
| --------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------- | ----- | ----------------------------------- |
| `model_not_found`                                   | Unknown model or alias. The compat `GET .../models/{id}` routes and unknown `priority_order` entries return it as `400`.   | No    | Check `GET /v1/models`              |
| `vendor_not_found`                                  | Unknown vendor on `GET /v1/vendors/{vendor_id}`                                                                            | No    | Check `GET /v1/vendors`             |
| `customer_not_found`                                | Unknown [Embedded Routing](/merge-gateway/embedded-routing-stack/overview) customer                                        | No    | Check the customer id               |
| `provider_credentials_missing`                      | The route needs your own vendor key and none exists for the customer or organization                                       | No    | Add the key the message names       |
| `previous_response_not_found`, `response_not_found` | The stored response expired (an hour after last use), belongs to another organization, or wasn't stored with `store: true` | No    | Send the full conversation          |
| `video_job_not_found`                               | Unknown video id                                                                                                           | No    | Check the id from `POST /v1/videos` |
| The vendor's own code                               | The vendor returned `404` (`source: provider`)                                                                             | No    | A routing policy fails over         |

## Too large: 413

| `code`              | Meaning                                                                                        | Retry | What to do          |
| ------------------- | ---------------------------------------------------------------------------------------------- | ----- | ------------------- |
| `payload_too_large` | Inline base64 media over 20 MB per request for images, documents, or video, or 25 MB for audio | No    | Send a URL instead  |
| `request_too_large` | A gzip body over 64 MB once decompressed                                                       | No    | Send a smaller body |

## Blocked by guardrails: 422

The body also carries `guardrails` when you set `include_guardrails_metadata`.

| `code`                                                         | Meaning                                                                                                                                                          | Retry | What to do                         |
| -------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----- | ---------------------------------- |
| `blocked_by_dlp_policy`                                        | A [DLP rule](/merge-gateway/security/data-loss-prevention) blocked the request (`type: invalid_request_error`)                                                   | No    | Remove the data or change the rule |
| `dlp_redacted_empty`                                           | The prompt was empty after DLP redaction                                                                                                                         | No    | Change the prompt                  |
| `pi_block_threshold`                                           | [Prompt injection protection](/merge-gateway/security/prompt-injection-protection) scored the input above the block threshold (`type: blocked_by_pi_protection`) | No    | Review the input or the threshold  |
| `pi_indirect_two_signals`, `pi_indirect_signal_and_structural` | Indirect injection was detected in tool results or other untrusted content                                                                                       | No    | Review the untrusted content       |
| `sidecar_error`                                                | The scan couldn't run and your policy fails closed                                                                                                               | Yes   | Retry                              |

## Rate limited: 429

| `code`                     | Meaning                                                              | Retry       | What to do                                                                   |
| -------------------------- | -------------------------------------------------------------------- | ----------- | ---------------------------------------------------------------------------- |
| (none), `source: provider` | The vendor throttled the request. A routing policy fails over first. | Yes         | Back off. No `Retry-After` is forwarded.                                     |
| (none), no `source`        | The key's `rate_limit_rpm` was reached                               | Yes         | Wait for `Retry-After`                                                       |
| `free_tier_daily_limit`    | A Free plan organization used its daily requests                     | After reset | Add a payment method. `Retry-After` is the seconds until the UTC day resets. |

See [Rate limits](/merge-gateway/rate-limits).

## Gateway and vendor failures: 5xx

| Status | `code`                                                                                                                                 | Meaning                                                                                                        | Retry | What to do                                    |
| ------ | -------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------- | ----- | --------------------------------------------- |
| 500    | `internal_error`                                                                                                                       | Unexpected Gateway error                                                                                       | Yes   | Contact support if it persists                |
| 500    | `response_store_unavailable`                                                                                                           | The stored conversation couldn't be read in time                                                               | Yes   | Retry, or send the full conversation          |
| 500    | `org_vendor_api_base_missing`, `org_vendor_credential_missing`, `org_vendor_credential_undecryptable`, `vendor_credential_key_missing` | A self-hosted endpoint or vendor credential is misconfigured                                                   | No    | Re-save the endpoint or credential            |
| 503    | `provider_authentication_failed`                                                                                                       | The vendor rejected the credential serving the route                                                           | No    | For BYOK, check your vendor key               |
| 503    | `provider_account_restricted`                                                                                                          | The vendor declined for account or billing reasons on your credential                                          | No    | Check your vendor account                     |
| 503    | `upstream_account_unavailable`                                                                                                         | The vendor declined on Merge-managed credentials                                                               | Yes   | Retry, or use another model                   |
| 503    | `model_vendor_unhealthy`                                                                                                               | The route is marked unhealthy after repeated failures                                                          | Yes   | Retry, or use a routing policy                |
| 503    | (none), `provider_error`                                                                                                               | Transient vendor failure, overload, or a stalled stream                                                        | Yes   | Retry with backoff                            |
| 503    | `model_resolution_unavailable`, `org_vendor_store_unavailable`, or (none) with `internal_error`                                        | An alias, self-hosted endpoint, or customer setting couldn't be read, so Gateway failed closed                 | Yes   | Retry                                         |
| 503    | (none), `service_unavailable`                                                                                                          | Gateway is starting up                                                                                         | Yes   | Retry                                         |
| 503    | `byor_no_qualifying_model`                                                                                                             | BYOR routing found no model that meets its thresholds and has no fallback. The error is nested under `detail`. | No    | Loosen the thresholds or set a fallback model |

## Batches

[Batch](/merge-gateway/capabilities/batch-inference) endpoints use the same envelope. Line-level errors name the `custom_id`.

| Status | `code`                                                                                                                                                                         | Retry | What to do                                         |
| ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----- | -------------------------------------------------- |
| 400    | `unsupported_batch_endpoint`, `empty_batch`, `invalid_custom_id`, `duplicate_custom_id`, `batch_model_mismatch`, `invalid_status_filter`, `invalid_created_filter`             | No    | Fix the batch or the query                         |
| 400    | `stream_unsupported`, `empty_messages`, `empty_input`, `max_tokens_required`, `invalid_request_body`, `unsupported_params`, `unsupported_parameter`, `unsupported_batch_media` | No    | Fix the line                                       |
| 402    | `batch_budget_exceeded`                                                                                                                                                        | No    | Add credits or wait for other batches to bill      |
| 403    | `batch_not_supported`                                                                                                                                                          | No    | Pick a model and endpoint with a batch price       |
| 404    | `batch_not_found`                                                                                                                                                              | No    | Check the batch id                                 |
| 409    | `batch_results_not_ready`, `batch_not_cancellable`, `batch_not_deletable`                                                                                                      | No    | Wait until the batch is in the right status        |
| 413    | `batch_body_too_large`, `batch_too_large`                                                                                                                                      | No    | Split the batch                                    |
| 422    | `blocked_by_dlp_policy`, `batch_media_blocked_by_dlp`, `web_search_unsupported`                                                                                                | No    | Remove the blocked data, media, or web search tool |
| 429    | `too_many_open_batches`, `batch_submit_busy`, `provider_batch_queue_full`, `provider_rate_limited`                                                                             | Yes   | Retry later, honoring `Retry-After`                |

## Management and platform APIs

The [Management API](/merge-gateway/management-api), the [Embedded Routing Stack](/merge-gateway/embedded-routing-stack/overview) API, the [Evals API](/merge-gateway/evals/evals-api), and the Migrations API return `{"detail": ...}`. Most have a string `detail` and a standard status: `400` validation, `401` missing or invalid key, `403` missing scope, `404` not found, `409` conflict, `422` schema. Cases worth branching on carry `{"detail": {"code", "message"}}`:

| Status | `code`                                                                                                                        | Where                                                                         | What to do                            |
| ------ | ----------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------- | ------------------------------------- |
| 400    | `customer_inactive`                                                                                                           | Create customer API key                                                       | Reactivate the customer               |
| 403    | `customer_scoped_key_forbidden`                                                                                               | Organization-level routes called with a customer API key                      | Use an organization-level key         |
| 403    | `customer_scoped_key_forbidden_field`                                                                                         | A customer API key changing anything but its own `name`, or its own controls  | Use an organization-level key         |
| 403    | `scoped_controls_loosening_forbidden`                                                                                         | A management key widening organization controls on a project                  | Make the change in the dashboard      |
| 409    | `org_api_key_limit_reached`                                                                                                   | Creating an API key when the organization already has 1,000                   | Delete unused keys or contact support |
| 429    | `customer_api_key_rate_limited`                                                                                               | Minting more than 10 API keys per minute for one customer                     | Retry after the `Retry-After` header  |
| 409    | `PROJECT_HAS_ACTIVE_API_KEYS`                                                                                                 | Deleting a project with active keys                                           | Revoke the project's keys first       |
| 503    | `DATA_PLANE_UNAVAILABLE`                                                                                                      | Deleting a project when its keys can't be checked                             | Retry                                 |
| 400    | `unknown_model`, `no_cases`, `unknown_case`, `duplicate_output`, `too_many_cases`, `input_too_large`, `duplicate_external_id` | Evals suites, case sync, and run triggers                                     | Fix the request as the message says   |
| 409    | `suite_name_taken`, `run_in_flight`, `not_cancellable`                                                                        | Evals: a name clash, 3 runs already in progress, or cancelling a finished run | Rename, wait, or skip the cancel      |
| 429    | (none)                                                                                                                        | Evals run triggers over 10 per minute per organization                        | Retry after a minute                  |
| 400    | `vendor_access_required`, `unknown_suite`                                                                                     | Migrations: a gated model, or a suite that doesn't exist                      | Request access or fix the suite id    |
| 409    | `baseline_has_override`                                                                                                       | Migrations: the baseline is already overridden by a completed migration       | Revert that migration first           |

## What fails over

Under a routing policy, vendor `429`, `5xx`, timeouts, billing errors, vendor `404`, `output_budget_exhausted`, and content-policy refusals move to the next route instead of reaching you, so client retries on them usually repeat work. Gateway's own `400` to `404`, credential `401` and `403`, and budget `402` stop ([failover rules](/merge-gateway/routing/using-policies#failover)).

Three kinds of `200` response also fail over, on non-streaming requests only:

| Response                                                                                                                                | Fails over to                                                                                |
| --------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------- |
| Empty content, no tool calls, and `finish_reason` `stop`, on a request without `stop` sequences                                         | The next candidate route                                                                     |
| `finish_reason` `content_filter` (a vendor safety filter blocked the prompt or the output) and no tool calls, even with partial content | A model from a different direct provider                                                     |
| A body that breaks a `strict` `json_schema`                                                                                             | A different model. See [Structured outputs](/merge-gateway/capabilities/structured-outputs). |

When every route returns one of these, you get the last response rather than an error. Each attempt is billed at the route that ran it.

## Errors during streaming

If Gateway rejects a streamed request or the vendor's first frame is an error, you get the same HTTP status and body as a non-streaming request, with no stream. Once the [stream](/merge-gateway/streaming) opens the status is `200`, and failures arrive as frames: `response.error` on native `/v1/responses`, `error` then `response.failed` on `/v1/openai/responses`, and an `error` event on the Anthropic surface.

Requests that run with changes, such as a dropped tool or a clamped effort level, succeed with a [`warnings`](/merge-gateway/warnings) array.

## Next steps

#### [Warnings](/merge-gateway/warnings)

Requests that succeed but run differently from what you sent

#### [Rate limits](/merge-gateway/rate-limits)

The limits Gateway enforces and how to handle a 429

#### [Routing policies](/merge-gateway/routing/overview)

Turn vendor failures into automatic failover