Docs menu

Errors

Errors come back in the dialect of the endpoint you called, so your SDK's own error classes work unchanged. Every error response — every response at all — carries an x-request-id header. Quote it when you write to support@princep.org.

The two envelopes#

OpenAI wire (/v1/chat/completions, and everything that is not a completion endpoint, including a 404 for an unknown path — /v1/messages/count_tokens is the one exception and speaks the Anthropic envelope):

json
{"error": {"message": "…", "type": "invalid_request_error", "param": null, "code": "invalid_api_key"}}

Anthropic wire (/v1/messages):

json
{"type": "error", "error": {"type": "authentication_error", "message": "…"}, "request_id": "…"}

The envelope is chosen from the path you requested, so a request that fails before it is even parsed still fails in the shape your client expects.

Anthropic error types#

On the Anthropic wire, error.type follows Anthropic's documented mapping from the HTTP status, which is what its SDKs' typed exceptions are built on:

Statuserror.type
400invalid_request_error
401authentication_error
402billing_error
403permission_error
404not_found_error
413request_too_large
429rate_limit_error
anything elseapi_error

Every status and code#

type and code are the OpenAI-wire values; the message is the same on both wires.

StatustypecodeparamWhenRetry?
400invalid_request_errorinvalid_bodyBody is not a JSON objectNo — fix the request
400invalid_request_errorinvalid_bodymodelmodel missing or not a stringNo
400invalid_request_errorinvalid_bodymax_tokensAnthropic wire: max_tokens missing or not positiveNo
400invalid_request_errorinvalid_cache_keyprompt_cache_key or x-session-idConversation key over 256 charactersNo
400 / 404 / 422invalid_request_errorinvalid_requestThe model rejected the request (too long, unsupported field, content filter); its message is relayedNo
401invalid_request_errorinvalid_api_keyKey missing, unknown or revokedNo — check the key
401invalid_request_erroraccount_suspendedThe account is not activeNo — contact support
402insufficient_quotainsufficient_creditsBalance is $0 or belowAfter adding credit
404invalid_request_errormodel_not_foundmodelmodel is not in the catalogNo — see GET /v1/models
400invalid_request_errorunsupported_parameterthe field/v1/responses given previous_response_id, conversation, background or an item_reference — the wire is statelessNo — send the full input
404invalid_request_errorunknown_urlUnrouted path or method (also /v1/embeddings)No
413invalid_request_errorrequest_too_largeBody over 5 MBNo — shrink the request
429rate_limit_errorrate_limit_exceededOver 300 requests/minute for the account, or capacity temporarily unavailableYes — after retry-after if present, else with backoff
429rate_limit_errortoo_many_failed_auth10 failed authentications in a minute from one IPAfter retry-after
500api_errorinternal_errorUnhandled failureYes, with backoff
502api_errorupstream_errorThe model's endpoint failedYes, with backoff
503api_errorno_upstream_accountNo capacity available for this model right nowYes, with backoff

Retry guidance#

  • Honour retry-after. It is sent on 429 rate_limit_exceeded when a reset time is known and always on too_many_failed_auth. The OpenAI, Anthropic and Vercel AI SDKs already do this.
  • Retry 5xx with exponential backoff, a few times, then surface the error. A 502 or 503 is transient by definition.
  • Do not retry 4xx except 402 after adding credit and 429 after waiting. A 400 will fail the same way every time.
  • Streamed errors. A failure before the first byte is a normal error response with one of the statuses above. A stream that has already started cannot change its status; if it ends early you receive a truncated stream without its terminator ([DONE] or message_stop) — treat a missing terminator as a failure and retry. See Streaming.

Relayed messages#

When the model itself rejects a request, its message is relayed so you can act on it — "prompt too long", an unsupported parameter, a content-policy rejection. Anything that would describe how Sator is served rather than what your request did wrong is replaced with a generic message; nothing you see is about our infrastructure. Request bodies are never echoed back.