ShvIA

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#

SurfaceShape
/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/mcpJSON-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 /v1 endpoints, a 401 always uses the OpenAI shape, including /v1/messages, whose other errors use the Anthropic shape.
  • A framework-level error on /v1, such as a 429 or a 404 for a route that does not exist, is JSON only when you send Accept: application/json. Every official SDK does. A bare curl receives an HTML page.

Status codes#

StatusMeaningRetry
400the request is malformed. On POST /api/v1/agent/chat it also means a model or server that does not existno. Fix the request
401the 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 insteadno. Check the key. See authentication
403your 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 dono. Change something first
404on 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 yoursno
413the upload is too largeno
422the body failed validationno. Read errors or message
429a request limit was reachedyes, after Retry-After seconds
501a feature this gateway does not provide, such as tools on the Responses endpointno
502the model server, or its provider, failed, or returned nothingyes, with backoff, a few times
503the service is unavailable, or every model of the chain failed in a way that is worth retryingyes, after Retry-After when it is sent
504the model stopped answering mid-replyyes, 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 /v1 endpoints and the chat stream, and the body field request_id, on POST /api/v1/agent/chat and 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": {…}}, then data: [DONE].
  • /v1/messages: event: error. A stream that ends without message_stop failed too.
  • /v1/responses: response.failed or response.incomplete as the last event.
  • /api/v1/chat: an error event, then done.

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-After when 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 4xx other than 429.
  • A retried non-streamed call is a new call: it is metered again.
  • Use one X-Request-Id per logical operation, and the same one on the retries, so that you can tell them apart in the logs.