Errors

An error keeps the shape of the format you used, and its message is written by zdr.sh. Every error carries x-request-id, retry-after when waiting helps, and x-should-retry.

The shapes#

An OpenAI-format request, on /v1/chat/completions, /v1/responses, /v1/tools/search, /v1/embeddings and /v1/models, gets:

json
{"error":{"message":"The model \"gpt-5\" does not exist or is not available to this key. GET /v1/models lists the models you can use.","type":"not_found_error","param":"model","code":"not_found_model"}}

type is the OpenAI type of the status: invalid_request_error, authentication_error, insufficient_quota, permission_error, not_found_error, request_too_large, rate_limit_error, timeout_error or server_error. code is the machine-readable class below, and param names the offending field when there is one.

An Anthropic-format request gets the Anthropic envelope with the request id:

json
{"type":"error","error":{"type":"not_found_error","message":"Claude models are not served here. Use a zdr.sh model id, or map Claude model ids to zdr.sh models in the key's settings."},"request_id":"019269d2-..."}

The Anthropic type of the status: invalid_request_error, authentication_error, billing_error for a 402, permission_error, not_found_error, request_too_large, rate_limit_error, overloaded_error for a 503, timeout_error, and api_error for the rest of the 5xx.

An error after a stream has started is an event of the stream, not a status: an OpenAI error chunk on chat, an Anthropic error event, and on Responses a response.failed event with status, retry_after_ms and x_should_retry.

The classes#

CodeStatusMeaningWhat to do
invalid_request400The body could not be parsed or fails validationFix the request; retrying it unchanged will not help
content_filter400A content or safety filter refused the request. A refusal at the edge may arrive as a 403 and that status is passed throughChange what you ask
injection_suspected400The key's injection guard is set to block and the request carried a high-severity findingRemove the flagged content, or set the guard to warn
stateless_only400A Responses request pointed at stored stateSend the whole conversation in input
context_length400The input does not fit the model's window. OpenAI-format clients get context_length_exceeded, which agents match to compact and retryTrim the conversation, or pick a larger window
request_too_large413The body is over 32 MiBSend less; move large files out of the request
auth401No credential, or the key is wrong, revoked or expiredCheck the key; the message says which
insufficient_balance402The balance cannot cover the hold the request needsTop up at zdr.sh
forbidden403The key may not do this: a model outside its allowed list, an IP outside the allowlist, a suspended accountCheck the key's settings
insufficient_scope403The key's scopes do not include this endpointUse a key with the endpoint's scope
web_tools_blocked403The key's settings block the web toolsTurn the key's web tools on, or send the image inline as a data: URL
not_found_model404No enabled model serves that idCheck the id against GET /v1/models
model_endpoint_mismatch400A chat model sent to embeddings, or an embedding model sent to chat, messages or responsesSend the model to the endpoint its kind belongs to
model_has_no_vision400The model does not accept the images the request carriesUse a model with vision, or remove them
model_has_no_tools400The model does not accept the tools the request carriesUse a model with supports_tools, or send no tools
image_generation_unsupported400A Responses request asks for the image_generation hosted toolSend the prompt without the tool
endpoint_not_supported404The path is an audio, image or rerank endpoint of another provider's API, which this one does not serveDo not retry; quickstart lists the endpoints
rate_limited429A per-key rpm or tpm was hit, or the account is at its concurrency capWait for retry-after; the x-ratelimit-* headers say which limit
daily_cap_reached429The key's daily spend cap is used up. Resets at 00:00 UTCWait, or raise the cap
monthly_cap_reached429The key's monthly spend cap is used up. Resets on the 1st at 00:00 UTCWait, or raise the cap
memory_tool_call_ceiling400The model asked for more memory calls in one round than the service allows; the request was refused before any of them ranAsk a narrower question, or retry
idempotency_key_reused400The Idempotency-Key was already used with a different bodyUse a new key for a different request
idempotency_in_progress409A request with this Idempotency-Key is still runningRetry when the first request has finished
idempotency_unreplayable409The first response cannot be replayed: over 4 MiB, or a stream that did not finishSend a new request with a new key
upstream_unavailable503No model could serve the request, or a dependency failedRetry with backoff; it is on our side
capacity_unavailable503Inference is paused while a shared resource drains. Nothing was chargedWait for retry-after and retry
timeout504The model did not answer in timeRetry; a streamed request is less exposed to it
internal500Something failed on our sideRetry once, then contact support with the x-request-id

Retrying#

  • A 429 and a 503 carry retry-after in whole seconds. A 503 without one carries two seconds.
  • Every error carries x-should-retry, true for a 429 and a 5xx.
  • 503 and 504 are safe to retry with exponential backoff. A request is settled for what it actually produced, so a retry after a failed stream does not pay twice.
  • A 400, 401, 402, 403 and 404 are not worth retrying unchanged.
  • A retry is a new request unless it carries an Idempotency-Key, which returns the first answer instead of charging again. Chat completions has the rules.

The request id is issued by the service and a client-sent value is ignored, so a request cannot settle twice under one id.