Skip to main content
The gateway sits between your SDK and the upstream LLM provider. It sends a request only to a provider that speaks your SDK’s wire format, so a provider’s error arrives in a shape close to what your SDK expects. Depending on the route, the gateway forwards the error body as is or rewrites it into your SDK’s error envelope, and it always adds headers that classify the error. This page covers what the gateway guarantees about error responses, when it rewrites an error body, and how to inspect the gateway’s classification headers for richer error handling.

Guarantees at a glance

Headers always classify

Every error that originates at a provider — or at the gateway’s auth layer — carries two stable headers, regardless of which provider, which SDK surface, or which routing strategy produced it:
The x-to11-error-code taxonomy is the canonical way to classify these errors. Values are stable across all combinations of upstream provider and surface SDK: Build retry, alerting, and metrics dashboards on these headers — not on response body text. The header value is the same whichever provider produced the error and whichever SDK you called through. Apart from feature_disabled and cross_dialect, errors raised by the gateway’s own routing and infrastructure layer — before any provider is contacted — carry no x-to11-error-code, because the taxonomy above classifies upstream conditions. Classify those by HTTP status plus the error.code field in the body (INFRA_UNAVAILABLE, SNAPSHOT_DECRYPT_FAILED, ENVIRONMENT_NOT_FOUND, ROUTING_NOT_CONFIGURED). See Gateway-side failures state their own retry hint.

Bodies from another provider match your SDK

Your SDK reaches a provider other than its own only when that provider speaks the same wire format — the OpenAI SDK calling Groq, Mistral, or Google Gemini through its OpenAI-compatible endpoint, for example. When such a provider returns an error the gateway recognizes, the gateway classifies it and re-emits it in your SDK’s error envelope, so your SDK’s built-in retry policy fires as it would against its own provider. On the OpenAI envelope, upstream overload and rate limiting both render as "type": "rate_limit_error" with "code": "rate_limit_exceeded", because OpenAI has no overload error type; this keeps the OpenAI SDK’s automatic retry working. The x-to11-error-code header still tells them apart (overloaded or rate_limit).

Retry-After is threaded through

When the upstream response carries a Retry-After header (integer-seconds form), the gateway re-emits it on its response. SDKs that honor Retry-After (the official OpenAI and Anthropic SDKs both do) back off accordingly. When the upstream omits the header, the gateway does not synthesize a default — Retry-After is absent on the gateway response too, and the SDK falls back to its own backoff policy.

Gateway-side failures state their own retry hint

The section above covers errors that originate at the provider. A failure inside the gateway’s own infrastructure — a 503 whose body carries "code": "INFRA_UNAVAILABLE" — states its retry hint explicitly instead of leaving your client to infer one from the status code:
  • x-should-retry: true — a positive signal that this request is safe to send again. The official OpenAI and Anthropic SDKs honor it.
  • retry-after-ms — the same delay at millisecond precision, for clients that read it. These failures typically clear within a round trip, so a whole-second value would overstate how long the condition lasts.
  • Retry-After: 1 — the standard header, floored to one second, for plain HTTP clients and proxies that only understand seconds.
The request never reached a provider, so retrying costs you nothing but the round trip and no tokens are billed. These responses carry no x-to11-error-code — that header classifies upstream provider failures, and no provider was contacted here. Branch on the status plus x-should-retry, or on error.code in the body. Not every gateway 503 is retryable this way. A rejected routing snapshot ("code": "SNAPSHOT_DECRYPT_FAILED") fails closed and only clears once the project’s configuration is republished — it deliberately omits x-should-retry and the sub-second hint, because retrying cannot resolve it. Read the headers rather than assuming every 503 behaves the same way.

Same-provider routes remain verbatim

When the surface SDK and the upstream provider match (OpenAI SDK → OpenAI upstream, Anthropic SDK → Anthropic upstream), the gateway forwards the upstream error body verbatim. This preserves any provider-specific signal — header fields, param indicators, request IDs — that the SDK might inspect beyond the standard envelope shape. The classification headers (x-to11-error-code, x-to11-upstream-provider) are still attached. The HTTP status is still verbatim.

Native passthrough remains byte-for-byte

Provider-native passthrough routes forward the upstream response byte-for-byte on both the success and error paths, giving you bit-identical compatibility with a provider’s native API.

5xx bodies are redacted

For an upstream 5xx, the response body’s message is rewritten to a conservative provider returned status N form. The upstream’s full message text never reaches the client, even if it would have carried useful debugging signal — that signal is preserved on the trace for the request, which you can inspect in the to11 dashboard (Projects → Traces), not in the response body. A generic upstream 5xx is surfaced to the client as 502 Bad Gateway. The one exception is capacity overload, which is surfaced as 529 (the status Anthropic uses) and classified as overloaded. Either way the x-to11-error-code header reaches the client, so your SDK can classify and retry without depending on body text.

Routing errors

These errors come from the gateway’s routing step, before any provider is contacted. Retrying the same request does not clear them; change the request or the provider’s configuration. See Naming the provider in a request and Access policy.

Wire-format mismatch

The gateway forwards a request to its provider as written, so the provider must speak the request’s wire format. An OpenAI SDK request to /v1/chat/completions that names an Anthropic provider returns:
The body has this {"error": {"message": ...}} shape whichever SDK sent the request, so branch on the status and the x-to11-error-code header. The wire formats are openai.chat (/v1/chat/completions), openai.responses (/v1/responses), and anthropic.messages (/v1/messages); which providers speak each is listed in Which SDK to call a provider with.

Examples

Inspecting x-to11-error-code for richer classification

When you need to differentiate quota exhaustion (retry will not clear) from rate limiting (retry with backoff clears) or from upstream overload, read the x-to11-error-code header. The body cannot always tell them apart: a body rewritten into the OpenAI envelope renders overload and rate limiting alike as rate_limit_exceeded, and a body forwarded verbatim carries whatever the provider put there.

Unrecognized envelopes

When an upstream returns an error shape the gateway does not recognize (an empty body, malformed JSON, or a new upstream error kind), it falls back to a generic error body rather than one in your SDK’s envelope. The x-to11-error-code header still classifies the error correctly, so retry and alerting logic built on the header keeps working regardless.

Mid-stream SSE error frames

When an upstream returns 200 OK for a streaming request and then emits an error partway through the stream, the gateway delivers that error as an SSE error frame in the shape your SDK expects, and marks the stream aborted so no false success terminator follows:
  • OpenAI surface — data: {"error":{...}} (the OpenAI SDK parses the top-level error key into the appropriate exception class).
  • Anthropic surface — event: error followed by data: {"type":"error","error":{...}} (the Anthropic SDK dispatches by SSE event name).
Same-provider and native-passthrough routes forward the upstream’s error frame verbatim; a frame from another provider in the same wire format is rewritten into your SDK’s shape, as on the status-time path. The HTTP status line stays at 200 OK — the error lives in the response body, not on the status line.
  • API Reference — endpoints, status codes, request/response shapes.
  • Telemetry — how error classification shows up in spans and metrics.
  • Streaming — error-handling on the streaming path.