LLM API error codes, explained and fixed
Every error your client throws maps to an HTTP status code, and each code points at a different layer: your key, your balance, your request rate, or the upstream model. This page is the quick reference for Router One's OpenAI-compatible API and the CLI tools that sit on top of it. Start with the final request row in Dashboard → Logs; if a provider retry needs deeper investigation, keep the request_id so support can correlate intermediate attempts in operational logs.
Quick reference
The first thing to check for each status code:
| Code | Meaning | First thing to check |
|---|---|---|
| 400 | Invalid request, or the wrong endpoint for that model | The message names the problem: a Responses feature the gateway does not run (background, image_generation), an incomplete response_format envelope, or a model that has to be called on another path — model '<id>' must be called via /v1/messages or /v1/chat/completions. |
| 401 | Invalid or missing API key | The Authorization header carries your key as `Bearer sk-…` — check for typos, extra whitespace, or an empty env var in the shell that runs your client. |
| 402 | Insufficient balance | Your wallet balance or the key's spend cap is exhausted. Top up, or raise the key's maxSpend in Dashboard → API Keys. |
| 403 | Forbidden | The key is disabled, or the request hit a permission boundary. Verify the key's status in the dashboard and confirm you're calling the right base URL for your client. |
| 404 | Unknown route or model | The path or model ID doesn't exist. Copy the exact model ID from the /models page — IDs are case-sensitive. |
| 429 | Rate limit exceeded | You hit the key's rateLimit / tokenLimitTpm ceiling or an upstream limit. Check Dashboard → Logs, slow the request rate, or contact support to raise limits. |
| 500 | Internal server error | Transient server-side failure. Retry with backoff; contact support if it persists. |
| 529 | Upstream overloaded | The model's upstream is saturated. If the error is retryable and another healthy provider serves the same exact model, Router One can try that route; otherwise retry with bounded backoff. |
| Timeout | No response in time | Long generations are normal for big prompts. Don't set aggressive client timeouts; check the final request metrics for end-to-end latency. |
Read the trace before guessing
Router One's customer-facing request row shows the final model and provider with tokens, cost, latency, and status. Dashboard → Logs filters by model and date, so you can see whether an error is isolated or recurring. It does not expose failed provider attempts or a fallback chain; share the request_id with support when those intermediate attempts need investigation.
Errors in Claude Code
Claude Code talks to the Anthropic-compatible endpoint at https://api.router.one — note: no /v1 suffix. Most 401/403s in Claude Code come from environment variables that are missing, exported in a different shell, or overridden by an earlier official-API login. Claude Code authenticates against a gateway with ANTHROPIC_AUTH_TOKEN; ANTHROPIC_API_KEY is not required and triggers an extra approval prompt when it is set. A 400 whose message reads model '<id>' must be called via /v1/chat/completions is a different problem: the model ID you configured is not served on the Anthropic-compatible endpoint, so pick one whose detail page lists POST /v1/messages (the Claude family, plus DeepSeek V4) or call that model on /v1/chat/completions instead. For the auth case, set these and restart the terminal:
export ANTHROPIC_BASE_URL=https://api.router.one export ANTHROPIC_AUTH_TOKEN=sk-your-router-one-key unset ANTHROPIC_API_KEY
Errors in Codex CLI
Codex CLI speaks the Responses API wire format, which many OpenAI-compatible relays don't implement — that's why Codex 404s against them. Router One supports wire_api = "responses" natively. Configure Codex in ~/.codex/config.toml with base_url = "https://api.router.one/v1", wire_api = "responses", and env_key = "ROUTER_ONE_API_KEY", then export ROUTER_ONE_API_KEY with your Router One key. Codex does not read OPENAI_BASE_URL or OPENAI_API_KEY for a custom provider, and requires_openai_auth makes it ignore env_key entirely. A 400 invalid_request that mentions background or image_generation is the gateway refusing two Responses features it does not run: drop background = true, and generate images through /v1/images/generations instead of the image_generation tool; hosted tools and previous_response_id work on models served natively over the Responses wire format. A 400 that reads model '<id>' must be called via /v1/messages or /v1/chat/completions is a different case: a Claude-family model ID reached the Responses endpoint, where Claude is not served — point Codex at a GPT-family model or DeepSeek V4 and use Claude Code for Claude. Full walkthrough on the Codex Responses API page.
FAQ
My key looks correct but I still get 401. Why?
The three usual causes: the env var is set in a different shell (or profile file) than the one running your client; the key was pasted with a trailing space or newline; or the client reads a different variable than the one you set. Print the variable in the same session right before launching the client to confirm.
What's the difference between 402 and 429?
402 is about money: the wallet balance or the key's maxSpend cap is exhausted. 429 is about speed: too many requests or tokens per minute for the key's rateLimit / tokenLimitTpm, or an upstream limit. The request trace in Dashboard → Logs shows which one you hit.
Do 5xx errors mean I have to write retry logic?
An eligible 5xx or timeout can be retried on another healthy provider serving the same exact model, when one is available. That is not guaranteed for every error, so use bounded backoff for failures returned to your app and contact support if the pattern continues.
I got HTTP 200 with an empty message. Should my client retry?
No — a 200 is a completed response, not a failure, so don't auto-retry it. A model sometimes spends tokens without writing an answer: a system prompt that tells it to stay silent, a reasoning pass that ends without output, or a refusal returned in message.refusal. Router One passes that reply through as it came back, and settlement follows the usage the response reports — a retry loop pays for the same silence again. Read finish_reason, refusal and usage on the reply, then adjust the prompt. A reply that reports no usage at all is the failure case: the gateway treats it as an upstream error, tries another candidate for the same model, and surfaces a 502 if none answers.
Why does Claude Code return 403 while the same key works with curl?
curl talks to the OpenAI-compatible endpoint; Claude Code needs the Anthropic-compatible base URL without the /v1 suffix, plus ANTHROPIC_AUTH_TOKEN. ANTHROPIC_API_KEY is not required — leave it unset. See the dedicated Claude Code 403 guide for the full checklist.
Where do I see exactly which request failed and why?
Dashboard → Logs shows the final request record with status, model, provider, tokens, and latency. Filter by model and time range to isolate it, then use its request_id when support needs to inspect failed intermediate provider attempts in operational logs.
I get unsupported_country_region_territory or insufficient_quota — are those Router One errors?
No — both come from the official OpenAI API, not the gateway. unsupported_country_region_territory is a region block on the platform edge; insufficient_quota is exhausted billing credit disguised as a 429. Each has a dedicated fix guide: the region error page and the insufficient_quota page.