Skip to Content
Errors

Errors

Errors are returned in your ingress format’s native error shape with the correct HTTP status code. Upstream provider failures are mapped to a stable taxonomy — the code values below never change meaning, so you can switch models and providers without rewriting error handling.

Error envelopes per format

OpenAI-format requests get the OpenAI error object:

{ "error": { "message": "Workspace balance is not positive. Top up to resume.", "type": "insufficient_credits", "code": "insufficient_credits", "param": null } }

HTTP status: 402 Payment Required.

The taxonomy

This table is rendered from the gateway’s own error constants (@hyperinfer/shared), so it cannot drift from the implementation:

CodeHTTP statusMeaning
provider_auth502The upstream provider rejected our credentials. Not caused by your key.
provider_rate_limit429The upstream provider rate-limited the request. Retry with backoff.
provider_overloaded529The upstream provider is overloaded. Retry with backoff.
context_length_exceeded400The request exceeds the model's context window.
content_filter400The upstream provider's content filter blocked the request or response.
provider_timeout504The upstream provider timed out before completing the response.
provider_unavailable502The upstream provider could not be reached or returned a server error.
insufficient_credits402The workspace balance is not positive. Top up to resume.
key_limit_exceeded402A daily, monthly, or total spend limit on this API key was reached.
model_not_allowed403The key is pinned to specific models and this slug is not on its list.
invalid_api_key401The key is missing, malformed, revoked, disabled, or expired.
workspace_locked403The workspace has been locked by operations.
rate_limit_exceeded429Too many requests per minute on this key. Honor Retry-After.
invalid_request400The request body failed validation for the ingress format.
payload_too_large413The request body exceeds the 50 MB limit.
internal_error500Unexpected error on our side. Safe to retry.

Retry-After — when to back off

Rate-limited and overloaded responses tell you exactly when to retry, two ways at once:

  1. The standard Retry-After HTTP header — integer seconds.
  2. An in-envelope retry_after_seconds field — the same integer, inside the error object, so SDKs that don’t surface headers can still read it.
HTTP/1.1 429 Too Many Requests Retry-After: 12 { "error": { "message": "Exceeded the per-key rate limit — retry in ~12s", "type": "rate_limit_error", "code": "rate_limit_exceeded", "param": null, "request_id": "req_…", "retry_after_seconds": 12 } }

The field is present iff the header is present — on these codes only:

CodeHTTPSource of the value
rate_limit_exceeded429token-bucket reset time
provider_rate_limit429upstream Retry-After (passed through)
provider_overloaded529circuit-breaker cooldown (30 s ± 20% jitter)
provider_timeout504upstream Retry-After, or breaker cooldown
provider_unavailable502upstream Retry-After, or breaker cooldown

On every other error the header and the field are both absent — do not assume they exist. The value is clamped to 1–120 seconds so a misbehaving upstream can’t push you out indefinitely. The message is human-readable and carries a friendly countdown ("retry in ~12s") for logging surfaces that only show the message.

Mid-stream errors (after the 200 has been sent)

Rate limits and credit checks are pre-flight only — if your key is over the limit or your workspace is out of credits, you get an HTTP 429/402 before streaming starts, not mid-stream. The gateway itself never terminates a stream for its own rate-limit or credit reasons once the 200 status and Content-Type: text/event-stream headers are sent.

However, if the upstream provider returns an error after streaming has started (e.g. an overloaded upstream, a malformed chunk, a network timeout), the error arrives as a mid-stream error frame in the format’s native SSE shape — you don’t get an HTTP status change (the 200 was already sent):

Chat Completions

data: {"id":"gen-…","object":"chat.completion.chunk","created":,"model":"…", "choices":[{"index":0,"delta":{"content":""},"finish_reason":"error", "error":{"code":"provider_unavailable","message":"…","type":"api_error","param":null, "request_id":"…","retry_after_seconds":12}}]} data: [DONE]

The finish_reason is "error" (not "stop"), the delta.content is empty, and the full error object is inlined on the choice as error. The retry_after_seconds field is present when applicable (same as the pre-flight 429).

Responses

event: response.incomplete data: {"type":"response.incomplete","response":{"id":"resp_…","status":"incomplete", "incomplete_details":{"reason":"error"},"error":{"code":"…","message":"…","param":null, "retry_after_seconds":12}}}

Anthropic Messages

event: error data: {"type":"error","error":{"type":"overloaded_error","code":"provider_overloaded", "message":"…","retry_after_seconds":12}}

In all three formats, the error frame is the last data frame before the stream closes. Partial usage (tokens generated before the error) is still billed and recorded — the status field on the usage event is "error", not "ok".

Explicitly rejected parameters

The gateway rejects rather than silently ignores parameters it cannot honor — you get 400 invalid_request in your format’s native error shape, so a request never quietly does something other than what you asked:

  • service_tier other than "auto" — the gateway owns tiering.
  • n > 1 (Chat Completions) — multiple choices per call are not supported.
  • modalities other than ["text"], the audio output param, and input_audio message content — this is a text-only deployment.
  • top_logprobs without logprobs: true (Chat Completions).
  • Stateful Responses paramsstore, background, conversation, previous_response_id, prompt — the gateway is stateless; send the full input.
  • Anthropic mcp_servers, container, context_management.

Every other top-level parameter you send is forwarded verbatim to the OpenAI-compatible upstream, never silently dropped — the full OpenAI sampling/tool/output surface (seed, frequency_penalty, presence_penalty, logit_bias, logprobs, parallel_tool_calls, per-tool strict, verbosity, prediction, …), OpenRouter extensions (min_p, top_a, repetition_penalty, provider, transforms), and any custom field.

Handling guidance

  • Retry with backoff: provider_rate_limit, provider_overloaded, provider_timeout, provider_unavailable, rate_limit_exceeded, internal_error. Honor the Retry-After header when present.
  • Fix the request: invalid_request, context_length_exceeded, payload_too_large, model_not_allowed.
  • Fix the account: insufficient_credits (top up), key_limit_exceeded (wait for the reset or raise the limit), invalid_api_key (rotate the key), workspace_locked (contact support).

The gateway itself retries upstream connect failures and pre-stream 5xx/429s at most twice with jittered backoff — and never retries once a response has started streaming, so you will never receive duplicated content.