Skip to main content

Error reference

The Ferro Labs AI Gateway returns errors in the same JSON shape as the OpenAI API, so existing OpenAI SDK error handling works unchanged. Because the gateway is a proxy in front of 30 upstream providers, every error has a source: it either originates in the gateway itself (bad request, auth, rate limit, plugin rejection) or is passed through from the upstream provider. The table below labels each row so you can tell the two apart โ€” the column that pure proxy users need most.

Error envelopeโ€‹

Every gateway-generated error is a single JSON object with one error field. The helper that writes it (internal/apierror) emits exactly three string fields:

{
"error": {
"message": "rate limit exceeded",
"type": "rate_limit_error",
"code": "rate_limit_exceeded"
}
}
FieldDescription
messageHuman-readable explanation of what went wrong.
typeBroad error category (invalid_request_error, authentication_error, permission_error, not_found_error, rate_limit_error, upstream_error, server_error).
codeStable, machine-readable identifier you can branch on (e.g. model_not_found, invalid_api_key, rate_limit_exceeded).
note

Errors the gateway generates carry Content-Type: application/json and this envelope. Two exceptions are not wrapped in the envelope: an upstream connection failure on a pass-through /v1/* route returns a plain-text 502 Bad Gateway body (proxy error: <reason>), and any response forwarded verbatim by the pass-through proxy keeps the upstream provider's own status code and body.

Status codesโ€‹

The Source column tells you where the error was decided:

  • Gateway โ€” the gateway rejected or could not route the request; the upstream provider was never contacted (or the gateway shaped the failure itself).
  • Provider โ€” the gateway forwarded the upstream provider's status and body unchanged (pass-through /v1/* routes return the provider's exact response).
HTTP Statustype / codeMeaningSourceWhat to do
400invalid_request_error / invalid_requestMalformed JSON body or failed request validation on /v1/chat/completions.GatewayFix the request body; ensure required fields like model and messages are present and valid.
400invalid_request_error / model_not_foundNo registered provider serves the requested model.GatewayUse a model offered by a configured provider; check GET /v1/models.
400invalid_request_error / streaming_not_supportedThe resolved provider for the model cannot stream.GatewayRetry with stream: false, or route the model to a streaming-capable provider.
400invalid_request_error / provider_not_resolvedPass-through /v1/* request with no provider resolvable.GatewaySet the X-Provider header (e.g. X-Provider: openai) or include a top-level model field in the body.
400invalid_request_error / request_rejectedA before_request plugin (e.g. word filter, max-token) rejected the request.GatewayAdjust the request to satisfy the guardrail, or change the plugin config.
401authentication_error / missing_api_keyMissing or non-Bearer Authorization header on a protected route.GatewaySend Authorization: Bearer <key>.
401authentication_error / invalid_api_keyThe bearer token is unknown or revoked.GatewayIssue or rotate a valid key.
401authentication_error / authentication_requiredA scope-checked route had no authenticated key in context.GatewayAuthenticate before calling the endpoint.
403permission_error / insufficient_scopeAuthenticated key lacks the required scope (e.g. read_only calling an admin write).GatewayUse a key with the admin scope, or request the needed scope.
404not_found_error / not_found_errorUnknown route or missing resource.GatewayCheck the path and HTTP method.
429rate_limit_error / rate_limit_exceededPer-IP token bucket exhausted, or a rate-limit plugin rejected, or the budget plugin's per-key USD limit was hit.GatewayBack off and retry. No Retry-After header is sent โ€” use client-side exponential backoff.
500server_error / routing_errorGeneric routing failure or an unclassified error from the routing layer (including circuit-open and upstream errors surfaced on native endpoints).GatewayRetry; if persistent, inspect gateway logs and provider health.
500server_error / internal_errorInternal configuration fault (e.g. a provider with an unparseable base URL on a proxy route).GatewayFix the provider configuration.
501invalid_request_error / proxy_not_supportedThe resolved provider does not implement proxy pass-through for this /v1/* endpoint.GatewayUse a provider that supports pass-through, or call a natively-handled endpoint.
502upstream_error / response_rejectedAn after_request plugin rejected the upstream response.GatewayLoosen the guardrail or fix what the provider returned.
502(plain text) proxy error: <reason>Upstream connection/transport failure while proxying a /v1/* request.ProviderTransient โ€” retry. If it persists, check provider availability and network egress.
503(forwarded verbatim)Upstream provider returned 503 Service Unavailable; the pass-through proxy forwards its status and body unchanged.ProviderRetry with backoff; the provider is overloaded or in maintenance.
tip

On pass-through /v1/* routes the gateway is transparent: any 4xx/5xx the upstream returns (including 429, 500, 502, 503) is forwarded with the provider's own status code and body. Treat those as Provider-sourced even though they reach you through the gateway.

Streaming errorsโ€‹

Once a streaming response has started, the HTTP status line is already 200 OK and headers are flushed, so failures cannot be reported with a new status code. Instead the gateway writes a final mid-stream data: event carrying the same error envelope, then closes the connection. There is no trailing data: [DONE] after an error event.

Event codetypeTriggerSource
stream_errorstream_errorThe upstream provider emitted an error chunk mid-stream.Provider
stream_timeouttimeout_errorNo chunk arrived within the idle window (2 minutes by default).Gateway

Example terminal error event:

data: {"error":{"message":"stream timed out waiting for next chunk","type":"timeout_error","code":"stream_timeout"}}

Client code should treat any streamed event whose top-level key is error as a terminal failure for that request. For the full wire format, the [DONE] sentinel, and idle-timeout details, see the Streaming (SSE) contract.