The HTTP Response will have the same status code as error.code, forming a request error if:
Your original request is invalid
Your API key/account is out of credits
Otherwise, the returned HTTP response status will be and any error occurred while the LLM is producing the output will be emitted in the response body or as an SSE data event.Example code for printing errors in JavaScript:
const request = await fetch('https://openrouter.ai/...');console.log(request.status); // Will be an error code unless the model started processing your requestconst response = await request.json();console.error(response.error?.status); // Will be an error codeconsole.error(response.error?.message);
On and responses, OpenRouter may include a standard HTTP Retry-After response header indicating how many seconds to wait before retrying.
HTTP/1.1 429 Too Many RequestsRetry-After: 60
The OpenAI SDK, Anthropic SDK, Vercel AI SDK, and OpenRouter SDK already respect this header for backoff. If you’re using fetch directly, honor it before retrying:
const res = await fetch('https://openrouter.ai/api/v1/chat/completions', { ... });if (res.status === 429 || res.status === 503) { const retryAfter = Number(res.headers.get('Retry-After')); if (Number.isFinite(retryAfter) && retryAfter > 0) { await new Promise((r) => setTimeout(r, retryAfter * 1000)); // retry the request }}
If your input was flagged, the error.metadata will contain information about the issue. The shape of the metadata is as follows:
type ModerationErrorMetadata = { reasons: string[]; // Why your input was flagged flagged_input: string; // The text segment that was flagged, limited to 100 characters. If the flagged input is longer than 100 characters, it will be truncated in the middle and replaced with ... provider_name: string; // The name of the provider that requested moderation model_slug: string;};
On inference endpoints (/chat/completions, /responses, /messages), a request can be blocked before it reaches a provider — for example by a content filter or prompt-injection detector configured via guardrails. When this happens, the response is a 403 with a message describing the block reason:
When you opt in to router metadata via the X-OpenRouter-Experimental-Metadata: enabled header, the 403 response also includes the full openrouter_metadata object with routing context and a pipeline array detailing the guardrail stages that ran:
OpenRouter normalizes every upstream provider error into the stable, typed
error_type vocabulary documented under Typed Error Codes.
The same error_type values describe what went wrong whether the provider
error arrives in a non-streaming response body or as a mid-stream SSE event.
Native protocol codes (the Anthropic error.type, the Responses error.code)
are best-effort and can differ between formats — error_type is the field to
rely on across all of them.For Chat Completions, a provider error that interrupts generation carries
error_type inside error.metadata:
The same value is carried on mid-stream errors and on the Anthropic and
Responses skins — see Skin-Specific Error Formats
for the exact wire location in each format.
When a request fails with a 500, the message is replaced with a generic
string and provider_code and openrouter_metadata are omitted, but
error_type is still present (server).For non-500 errors, the upstream provider’s own error code is surfaced in
error.metadata.provider_code when available. Opt-in routing context (which
provider was selected, fallback attempts, etc.) is carried in the
openrouter_metadata object when the request sets X-OpenRouter-Metadata
— it follows the same shape as on successful responses (routing-summary
fields only; see
Pipeline Stages).
Occasionally, the model may not generate any content. This typically occurs when:
The model is warming up from a cold start
The system is scaling up to handle more requests
Warm-up times usually range from a few seconds to a few minutes, depending on the model and provider.If you encounter persistent no-content issues, consider implementing a simple retry mechanism or trying again with a different provider or model that has more recent activity.Additionally, be aware that in some cases, you may still be charged for the prompt processing cost by the upstream provider, even if no content is generated.
Errors that occur before any tokens are sent follow the standard error format above, with appropriate HTTP status codes. At this stage the HTTP response hasn’t been committed yet, so OpenRouter can:
Return a proper HTTP error status (4xx/5xx)
Silently retry with a different provider endpoint if fallback routing is enabled
Apply rate-limit or auth checks before any work begins
You’ll see pre-stream errors for issues like invalid API keys, malformed requests, or when every available provider endpoint is exhausted before streaming starts.
Once the first token has been written to the client, the HTTP 200 OK status and headers are already committed — they can’t be changed. If the provider fails at this point, OpenRouter cannot silently fail over to another provider because partial content has already been delivered to your application. The error must arrive in-band as an SSE event.Common causes of mid-stream errors:
Provider disconnect — the upstream connection drops after partial output (network issue, provider crash, load balancer timeout)
Provider timeout — the model stops responding mid-generation and the read deadline expires
Token limit hit during generation — the model reaches max_tokens or the context window fills up while producing output
Output content filter — a content moderation system flags generated text after some of it was already streamed
Provider overload — the upstream returns a rate-limit or capacity error after beginning to stream
If an error occurs before any tokens are written — even on a streaming request — OpenRouter can still retry with a backup provider transparently. Mid-stream errors only happen when partial content has already been committed to your stream, making failover impossible.
Mid-stream errors are sent as Server-Sent Events (SSE) with a unified structure that includes both the error details and a completion choice:
When a provider error reaches your application, OpenRouter tags it with a
canonical error_type string — both on the non-streaming response body and
on mid-stream SSE events. Use this value, not the HTTP status code alone, to
programmatically distinguish error categories. It is stable across all three
API skins even when the native protocol code is lossy.Where error_type appears depends on the skin and path:
Chat Completions: error.metadata.error_type — on the mid-stream error
chunk (see Mid-Stream Errors) and on the non-streaming
response when a provider error interrupts generation.
Anthropic Messages: error.error_type on the SSE error event and the
non-streaming error envelope.
Responses: top-level error_type on the failed response, for both the
streaming response.failed event and the non-streaming JSON body.
The HTTP status each error_type maps to is listed in the tables below.
OpenRouter exposes three API skins. Each translates the same internal provider error types into its own wire format, for non-streaming responses and in-stream errors alike. In every case error_type is the stable field; the wire location differs per skin.
Mid-stream errors appear as a chat.completion.chunk with a top-level error object (shape shown above). The error.metadata.error_type field carries the typed code.For non-streaming requests where a provider error occurs, the error is embedded in the final response alongside any partial content:
The Responses API maps internal error types to the OpenAI Responses error code set. The mapping is narrower — many distinct internal types collapse to server_error — so the precise reason is preserved in a top-level error_type field on the response, outside the native error object:
Internal error_type
Responses API code
rate_limit_exceeded
rate_limit_exceeded
context_length_exceeded, invalid_request
invalid_prompt
content_policy_violation
image_content_policy_violation
authentication, provider_overloaded, provider_unavailable, timeout, server
server_error
All others (including invalid_prompt)
server_error
Both the streaming terminal event and the non-streaming JSON body carry the canonical error_type at the top level of the response object. For example, an authentication failure collapses to the native server_error code but keeps error_type: "authentication":
Because the native error.type is lossy (many internal types collapse to api_error), the canonical error_type is added inside the error object alongside it. This holds for both the non-streaming error envelope and mid-stream SSE error events.Non-streaming error envelope:
OpenRouter provides a debug option that allows you to inspect the exact request body that was sent to the upstream provider. This works with both the Chat Completions API (/api/v1/chat/completions) and the Responses API (/api/v1/responses). Useful for understanding how OpenRouter transforms your request parameters for different providers.
When debug.echo_upstream_body is set to true, OpenRouter sends a debug chunk as the first chunk in the streaming response. This chunk has an empty choices array and includes a debug field with the transformed request body:
Streaming OnlyThe debug option only works with streaming mode (stream: true). Non-streaming requests will ignore the debug parameter.
Not for ProductionThe debug flag should not be used in production environments. It is intended for development and debugging purposes only, as it may potentially return sensitive information included in the request that was not intended to be visible elsewhere.
Understanding Parameter Transformations: See how OpenRouter maps your parameters to provider-specific formats (e.g., how max_tokens is set, how temperature is handled).
Verifying Message Formatting: Check how OpenRouter combines and formats your messages for different providers (e.g., how system messages are concatenated, how user messages are merged).
Checking Applied Defaults: See what default values OpenRouter applies when parameters are not specified in your request.
Debugging Provider Fallbacks: When using provider fallbacks, a debug chunk will be sent for each attempted provider, allowing you to see which providers were tried and what parameters were sent to each.
OpenRouter will make a best effort to automatically redact potentially sensitive or noisy data from debug output. Remember that the debug option is not intended for production.