Skip to content
Open app

Errors

The error envelopes, status codes and error codes the API returns, and how to fix each.

Errors from the endpoints that reach a model, and from Key, carry x-request-id. Messages errors also carry request-id with the same value as request_id in the body. Quote it when you contact support.

A request that Deference forwarded to a provider appears in Activity under that id, including one the provider failed.

A request that Deference refused before forwarding it, with a 400, 401, 402, 404, 413, 429 or 503 of its own, does not appear there.

GET /v1/models/{id} answers an unknown model with a 404 that carries no request id.

OpenAI envelope

Chat Completions, Responses, Embeddings, Images, Models and Key return this shape.

{
  "error": {
    "message": "This account has no credit left. Add credit in the dashboard to continue.",
    "type": "insufficient_credit",
    "code": "insufficient_credit",
    "param": null
  }
}

Anthropic envelope

Messages returns this shape, which Claude Code parses.

{
  "type": "error",
  "error": {
    "type": "billing_error",
    "message": "This account has no credit left. Add credit in the dashboard to continue."
  },
  "request_id": "req_01K7Z4F2XQ"
}

Status and type

These types apply to errors generated by Deference. Provider errors follow the rules below.

StatusOpenAI typeAnthropic type
400invalid_request_errorinvalid_request_error
401authentication_errorauthentication_error
402insufficient_creditbilling_error
403permission_errorpermission_error
404invalid_request_errornot_found_error
409api_errorconflict_error
413invalid_request_errorrequest_too_large
410api_errorapi_error
429rate_limit_errorrate_limit_error
502api_errorapi_error
503api_erroroverloaded_error
504api_errortimeout_error

Codes

Codes are in error.code in the OpenAI envelope. The Anthropic envelope carries the type only.

StatusCodeCauseFix
400invalid_jsonThe body is not valid JSONSend a JSON object
400missing_modelThe request has no modelAdd a model id
400missing_promptCompletions: the request has no promptSend a prompt string or array
400invalid_parameterImages: n is not a whole number from 1 to 10Send a valid n
400output_cap_requiredThe model lists no maximum output and the request sent no output capSend max_tokens on Messages or max_output_tokens on Responses
400unsupported_parameterThe body sets models, fallbacks, route or provider, a plugins entry outside the free list (response-healing, file-parser on cloudflare-ai or native, context-compression with enabled: false), a tool type other than function, custom or a supported web-search tool, X search or audio input, or sends a file to a model without file input and no free parserName one model and drop those fields. For files, Chat Completions adds the free cloudflare-ai parser itself; Messages, Responses, Embeddings, Images and Completions refuse the file unless the request already sets it
400unsupported_modelThe model bills per search, such as perplexity/sonar-deep-researchUse another model
401missing_api_keyNo key was sentSend Authorization: Bearer sk-df-... or x-api-key
401invalid_api_keyThe key is not recognizedCopy the key again, or create a new one
401api_key_disabledThe key is disabledEnable it in API keys, or use another key
401api_key_expiredThe key passed its expiryCreate a new key
402insufficient_creditThe account has no available credit, or less than the request's hold. A file, or an image or video given as a URL, is held at the larger of the text estimate and the model's context window, or 1,000,000 tokens when that window is unknownAdd credit, lower max_tokens, or send fewer files and URL inputs
402key_limit_reachedThe key hit its credit limit, or has less left than the request's holdRaise the limit, lower max_tokens or use another key
402free_credit_not_eligibleThe account has only free credit, and this model or request does not qualifyUse an open-weight text model, set a lower max_tokens, or add credit
404model_not_foundThe model id is not in the catalogList models and pick a current id
413request_too_largeThe body is over 32 MB, or 64 MB on ImagesSend a smaller body
429rate_limitedMore than 300 requests in a minute on this key, or more than 20 across the keys of an account that has not yet bought or activated creditWait retry-after seconds; adding credit lifts the 20-per-minute cap
429too_many_concurrent_requestsThis key already has 8 requests in flightRetry when one finishes
502upstream_errorThe model provider returned an error, or the connection never openedRetry; nothing is charged
502upstream_errorA non-streamed answer was lost after the provider accepted itRetry. Nothing is charged yet, and the hold stays reserved
503service_unavailableInference is temporarily unavailableRetry shortly
503model_prices_staleThe model's prices have not been refreshed for over an hour, so the request cannot be pricedRetry later, or use another model
504upstream_timeoutThe provider did not answer before the deadline: 120 seconds on Images, 15 minutes on other non-streamed calls, and 5 minutes whenever response headers have not arrived or the body stallsRetry. Nothing is charged yet, and the hold stays reserved

free_credit_not_eligible explains itself: its message lists what free credit pays for and suggests max_tokens. See Free credit.

The table covers the model endpoints and Key. The credit-sale GETs answer their own codes — 400 invalid_parameter, 404 not_found, 502 chain_unavailable and 502 quote_unavailable — and the retired routes answer 410 endpoint_retired.

A non-streamed timeout charges nothing yet and keeps its hold reserved. A streamed timeout before response headers returns 504 and costs zero. The OpenAI error.code is upstream_timeout. Messages uses error.type: timeout_error. After headers, a timeout interrupts the HTTP 200 stream. A reported cost is charged plus OpenRouter's 5% platform fee. Deference adds no markup. Token counts with a generation id are a provisional estimate. Anything else does not charge the hold.

Provider errors

Provider errors that describe your request keep their status: 400, 403, 404, 408, 409, 413, 422 and 429. Deference rebuilds the body in the endpoint's error envelope and removes provider metadata.

Provider messages are replaced with approved text. Recognized context-length, content-policy, invalid-parameter and unsupported-parameter errors explain how to change the request. Context-length errors keep the phrase prompt is too long, which Claude Code recognizes. Other errors use a fixed message for the status. Provider diagnostics and token counts are not copied into messages.

On Messages, a valid Anthropic error keeps its error.type. Other bodies use Deference's type for the status. A provider 409 without a valid Anthropic error uses conflict_error. The body uses Deference's request_id, also sent in x-request-id and request-id.

A provider 401 or 402, which concerns Deference's own upstream account, returns 503. Any other provider failure returns 502.

These errors carry your request id and never reveal the upstream account.

Failures after a stream starts

A stream that fails midway still returns HTTP 200. When the provider sends an error event it is relayed in the stream; when the upstream connection drops, the stream simply ends with no error event. Activity marks that request failed.

A failed request is charged the provider's reported cost plus OpenRouter's 5% platform fee. Deference adds no markup. Token counts with a generation id are a provisional estimate. Anything else does not charge the hold. See Streaming events.

Retry

Retry 429, 502, 503 and 504 with backoff, and honor retry-after when present. It is a whole number of seconds.

Do not retry 400, 401, 402, 404 or 413 until you fix the cause. See Rate limits.