Errors

The error envelope and what every code we emit actually means.

The error envelope

The same shape OpenAI returns, so your existing error handling works. The code field is the precise one — branch on it, never on the message text.

JSON
{
  "error": {
    "message": "insufficient balance",
    "type": "insufficient_quota",
    "code": "insufficient_balance"
  }
}

Codes

۴۰۰ — Bad request

codeMeaning and what to do
invalid_requestThe body is malformed — the message names the field.
request_exceeds_token_limitprompt + max_tokens exceeds your whole tokens-per-minute allowance. Retrying never fixes this: shorten the prompt or lower max_tokens.
streaming_disabledStreaming is temporarily off. Re-send with stream=false.
invalid_provider_prefsThe provider field is malformed or names an unknown provider.
upstream_bad_requestThe upstream refused the body; the message is the upstream's own.

۴۰۱ — Authentication

codeMeaning
invalid_api_keyWrong, revoked or expired key. The most common real cause is a stray space or newline from copying.

۴۰۲ — Balance or quota

codeMeaning and what to do
insufficient_balanceNot enough balance to cover this request's hold. Top up — or lower max_tokens so the hold shrinks.
monthly_cap_reachedThis key's monthly spend cap is exhausted.
plan_quota_reachedThe plan's token allowance is spent and the plan allows no overage.
overage_disabledAllowance spent and overage is not enabled for this account. Enable it, or switch the key to credit.
overage_limit_reachedThe overage ceiling is exhausted.
guardrail_budget_exceededA guardrail budget of your organization is used up; its id is in the X-IranRouter-Guardrail header.
project_monthly_cap_reachedThe project's monthly spend cap is exhausted.

۴۰۳ — Permission

codeMeaning and what to do
guardrail_blockedA content guardrail of your organization refused this request (prompt injection or sensitive information). The message names the category only, never the text.
model_not_allowed_by_guardrailA guardrail of your organization does not allow this model (X-IranRouter-Guardrail header).
model_not_allowedThis key is deliberately restricted to a specific model list.
model_not_in_planThe model is outside your plan. Switch the key to credit to reach every model from your wallet — details.
byok_disabledBring-your-own-key is switched off platform-wide.
model_not_in_free_tierThe free plan reaches only the free models.

۴۰۴ / ۴۲۹ / ۵۰۳

codeHTTPMeaning
invalid_preset400A preset is named by its slug: lower-case letters, digits and dashes.
storage_unavailable507 / 503File storage is unavailable: 507 on upload (sometimes briefly, sometimes because this service is not keeping files right now — the message says which), 503 on read.
preset_not_found404No preset with this slug in this key’s workspace. Presets belong to a workspace (@preset/slug).
context_length_exceeded400The conversation's text cannot fit the model's context window even on the most generous count. This refusal itself costs nothing (but a file-parser OCR that ran before it was a request of its own, and was charged). Shorten it, or send transforms: ["middle-out"].
file_parser_failed / file_parser_unavailable / too_many_files4xx / 502The file-parser plugin: the document could not be read or yielded no text (with the OCR request's own status), the plugin is not configured on this service, or more than 4 PDFs in one request.
file_parser_daily_pages429The free plan's daily file-parser page allowance is spent; Retry-After runs to midnight, Tehran time.
invalid_plugins400An unknown plugin or transform name. Known: web, response-healing, file-parser and middle-out.
model_not_found404Unknown model ID — either it does not exist or it is unpublished. For iranrouter/auto: an unknown tier, or a tier that currently has no models.
rate_limit_rpm / rph / rpd / tpm429That window's rate limit is full. Honour retry-after.
key_daily_token_budget_reached429The key's daily token budget. Resets at Tehran midnight.
key_daily_budget_reached429The key's daily Rial budget.
free_daily_limit429The free plan's daily ceiling.
global_throttle429A temporary platform-wide throttle during a spike.
no_capacity503No healthy route for this model right now. Retry shortly.
gateway_paused503The service is in maintenance mode.
pricing_unavailable503No active rate to price with; we would rather not charge blind.
byok_rate_limited429Your own provider's rate limit on a BYOK key.
server_overloaded / load_shed503Concurrency is full, or this plan's traffic is shed during a spike. Honour retry-after.
model_in_maintenance503The model is temporarily out of service.

۴۰۹ / ۴۱۰ / ۴۱۳ / ۵۰۲

codeHTTPMeaning
idempotency_in_flight409The original request with this Idempotency-Key is still running.
idempotency_completed_unreplayable409The original completed, but its response was too large to replay.
model_sunset410The model is deprecated and past its sunset; migrate to the successor (X-IranRouter-Successor header).
payload_too_large413The body or upload exceeds the size ceiling.
upstream_error:*502The upstream failed after every attempt; the suffix names the kind. Nothing was charged.

Retry pattern

Which are worth retrying and which are not:

  • Retryable: 429 (after retry-after), 503 no_capacity, 5xx upstream — with exponential backoff.
  • Pointless: 400, 401, 403, 404 — the answer will not change until the request does.
  • Needs a human: 402 — a top-up or a settings change is required.

When you contact support

Send the x-request-id header value. With it we find the exact request — without it we are guessing.