Docs · Reference
Errors
Each surface reports errors in the shape its clients expect, so there is no single error format. This page lists the shapes, what each status means, and what is safe to retry.
The shapes#
| Surface | Shape |
|---|---|
/v1/chat/completions, /v1/responses, /v1/audio/*, /v1/models | {"error": {"message", "type", "param", "code"}} |
/v1/messages | {"type": "error", "error": {"type", "message"}} |
/api/v1/*, authentication failure | {"message": "Unauthenticated."} |
/api/v1/*, validation failure (422) | {"message": "…", "errors": {"field": ["…"]}} |
/api/v1/auth/login | {"status": "error", "message": "…", "errors": {…}} |
/api/v1/agent/chat, /api/v1/embeddings, and others | {"detail": "…"} |
/api/v1/conversations | {"status": "error", "message": "…"} |
/api/v1/collections | {"status": "error", "code": "…", "message": "…"}, see collections |
/api/v1/chat (stream) | an error event inside a 200 stream, see chat stream |
/api/v1/mcp | JSON-RPC errors inside a 200, see MCP server |
a request limit (429) | {"message": "Too Many Attempts."}, or {"codigo": "limite_de_requisicoes", "detail": "…"}, see rate limits and quotas |
Two details that surprise people:
- On the
/v1endpoints, a401always uses the OpenAI shape, including/v1/messages, whose other errors use the Anthropic shape. - A framework-level error on
/v1, such as a429or a404for a route that does not exist, is JSON only when you sendAccept: application/json. Every official SDK does. A barecurlreceives an HTML page.
Status codes#
| Status | Meaning | Retry |
|---|---|---|
| 400 | the request is malformed. On POST /api/v1/agent/chat it also means a model or server that does not exist | no. Fix the request |
| 401 | the credential is missing, wrong, expired or revoked, or its account has no API access or is deactivated. On /api/v1, a session token in those last two cases gets 403 instead | no. Check the key. See authentication |
| 403 | your account group's limits refused the call: a model that is not allowed, a token quota that is used up, or remote servers that the group may not use. Or an operation that an API key may not do | no. Change something first |
| 404 | on the /v1 endpoints, an id that names a server which does not carry the model (a bare name that does not exist is answered by another model, see models). Anywhere, a route, conversation or response that does not exist or is not yours | no |
| 413 | the upload is too large | no |
| 422 | the body failed validation | no. Read errors or message |
| 429 | a request limit was reached | yes, after Retry-After seconds |
| 501 | a feature this gateway does not provide, such as tools on the Responses endpoint | no |
| 502 | the model server, or its provider, failed, or returned nothing | yes, with backoff, a few times |
| 503 | the service is unavailable, or every model of the chain failed in a way that is worth retrying | yes, after Retry-After when it is sent |
| 504 | the model stopped answering mid-reply | yes, with backoff. The partial reply is in partial |
The fields that identify a call#
Keep these when you report a problem:
X-Request-Id, on every response. It identifies the HTTP request. You can send your own, up to 64 letters, digits,-and_, and it is returned unchanged, which lets you follow a call through your own systems.X-Inference-Request-Id, on the/v1endpoints and the chat stream, and the body fieldrequest_id, onPOST /api/v1/agent/chatand in the events of the chat stream. They identify the model call.
Provider statuses on agent/chat#
When every model of the chain fails, POST /api/v1/agent/chat answers 503 with Retry-After and retry_after for any failure that is worth retrying: a rate limit, a used-up provider quota, an overloaded provider, a refused or missing provider key, a server error, or a server that cannot be reached. It does so whatever status the provider sent, and detail ends with when to try again. A 503 here is not the gateway being down, and a refused provider key is not your gateway key. A request that the provider rejects as invalid keeps the provider's own status, usually 400.
The /v1 endpoints differ. A cloud provider's refusal keeps its status there only when no other model of the chain can answer; otherwise the default model answers with a 200 (see models). /v1/chat/completions maps the statuses of the operator's own servers, as its page says.
Errors that arrive late#
A streamed reply has already answered 200 when it fails, so its errors travel inside the stream:
/v1/chat/completions:data: {"error": {…}}, thendata: [DONE]./v1/messages:event: error. A stream that ends withoutmessage_stopfailed too./v1/responses:response.failedorresponse.incompleteas the last event./api/v1/chat: anerrorevent, thendone.
In every case the text that arrived before the failure was real. When a model stops mid-reply (504, read_timeout), the non-streamed endpoints return it in partial, and the native chat saves it.
Retrying well#
- Wait for
Retry-Afterwhen it is present. It is a number of seconds. - Otherwise back off exponentially with jitter, and give up after a few tries.
- Do not retry a
4xxother than429. - A retried non-streamed call is a new call: it is metered again.
- Use one
X-Request-Idper logical operation, and the same one on the retries, so that you can tell them apart in the logs.