Troubleshooting

Every status FreeRouter returns, what usually caused it, and the fix. Find the HTTP status in the table, then the code from the error body.

Where the code lives Keys with the openai or openrouter shape get { "error": { "message", "type", "code" } }. The anthropic and google shapes get their own SDK's error envelope, so read the message there — the same code shows on the request's row in Logs. Every response carries X-FreeRouter-Request-Id; quote it when you contact support. The envelope per shape is in API Reference → Errors.

Status and code table

StatuscodeLikely causeFix
400invalid_jsonThe body is not valid JSON (unbalanced quotes, a trailing comma, shell quoting).Validate the body; send Content-Type: application/json.
400missing_model / missing_messagesA chat body without model, or with an empty messages array.Send both fields.
400batch_only_modelA :batch model id on a synchronous endpoint.Submit it to POST /v1/batches, or drop the :batch suffix.
400(google envelope):streamGenerateContent — streaming is not served on the google shape.Call :generateContent without streaming, or use an openai-shaped key.
401invalid_api_keyNo key arrived, or it matched nothing. Common: the env var is empty, the key was pasted with a trailing space, or your SDK sends it in a header FreeRouter does not read.Send Authorization: Bearer fr_live_…, x-api-key (Anthropic SDK) or x-goog-api-key (Gemini). echo $FREEROUTER_API_KEY first.
401revoked_api_keyThe key was revoked in the dashboard.Create a new key. A rotated key keeps answering to its old secret until X-FreeRouter-Key-Grace-Until; a revoked one does not.
402no_routeThe key has no routing targets. X-FreeRouter-Attempts: 0 — nothing was sent upstream.Open the key card and route it to a provider. The create form does this for you under Route this key to.
402all_targets_failedEvery target answered 402: the gateway account behind your provider key is out of credit. X-FreeRouter-Attempts is 1 or more.Add credit on the gateway, or send a free model such as openrouter/free.
403ip_not_allowedAn IP allowlist (workspace or key) does not admit the caller. The message echoes the address FreeRouter saw.Add that address under Settings or on the key — see IP allowlists.
403workspace_suspendedThe workspace is suspended.Contact support.
404wrong_api_shapeThe endpoint does not match the key's shape — for example /v1/messages with an openai key, or /v1/chat/completions with an anthropic key.Call the shape's own endpoint, or create a key with the shape your SDK speaks. See API shapes.
404model_not_availableNone of the providers the routing rule picked list that model. Nothing was sent; the message names gateways that do list it.Add such a provider to the key's rule, or add a model remap.
404model_not_foundA gateway was asked and said the model does not exist. The message relays the gateway's text.Check the id against GET /v1/models for that key.
404not_foundA path FreeRouter does not serve: /v1/completions, /v1/files, /v1beta/….See what is not supported.
409response_chain_target_unavailableA previous_response_id is pinned to a provider that is no longer on the key.See Responses chaining.
413request_too_largeThe JSON body is over 10 MB.Send files by URL, or split the request.
429rate_limitedEvery target answered 429. Retry-After is forwarded when the gateway sent one. FreeRouter itself has no rate limit.Back off and retry; add a second provider so the next 429 fails over instead.
499client_closed (Logs only)Your client hung up before the response finished — usually an HTTP client timeout shorter than the model's answer. The Status column is that outcome. A hop in the expanded route can still show 200: that badge is the gateway's HTTP status, from before the client left.Raise the client timeout, or stream so bytes arrive early. Nothing is sent as HTTP 499.
501gateway_not_supportedNone of the key's providers can serve this endpoint (for example a Decisions-only host on a chat key).Route the key to a chat gateway from Providers.
501embeddings_unsupported/v1/embeddings: none of the gateways the routing rule picked has an embeddings route, so nothing was sent. Each was skipped as no-embeddings; the message names the gateways that do serve embeddings.Add one of those providers to the key's rule — see API Reference → embeddings.
502provider_auth_failedThe gateway rejected your stored provider key (401 upstream). Your FreeRouter key was fine.Re-paste the provider key under Providers.
502upstream_unreadableThe gateway closed the connection before a body arrived.Retry. If it repeats, check that gateway's status page and the row in Logs.
502stream_abortedA stream was cut off mid-way. Arrives as one data: {"error": …} record inside the stream, after a 200.Retry. Read the last chunk's error before treating a stream as complete.
502no_healthy_targetEvery target was unusable before a request was sent (no decryptable provider key).Check the providers on the key's rule.
502 / 5xxall_targets_failedEvery target failed; the last gateway's status is relayed (402, 403, 5xx).Read X-FreeRouter-Failover for the per-gateway reason, then retry.
503gateway_disabledFreeRouter has switched that gateway off platform-wide.Wait, or add another provider to the rule so traffic fails over.
504deadline_exceededThe 95 s request deadline ran out before any gateway answered: the targets tried so far used up the time, and the last attempt was cut short or never started. X-FreeRouter-Failover shows deadline on it.Put a faster target first, or give the slow one a shorter failover timeout so the next target gets its turn sooner.
500internalSomething broke inside FreeRouter.Retry; report the X-FreeRouter-Request-Id.

Reading the failover headers

Three response headers say what the router did. They are on error responses too.

HeaderMeaning
X-FreeRouter-ProviderThe gateway that answered. Absent when nothing was sent upstream (no_route, wrong_api_shape, auth errors).
X-FreeRouter-AttemptsHow many upstream calls were made. 0 means the request never left FreeRouter; 2 or more means at least one failover.
X-FreeRouter-FailoverOnly present after a failover: gateway:reason for each losing attempt, comma-separated, for example openrouter:ttfb-timeout,vercel:rate-limited.

Reasons: ttfb-timeout (no first byte within the failover timeout), timeout (upstream timeout), deadline (the 95 s request deadline cut this attempt short, or it was never started), unreachable, client-closed, not-listed (the gateway's catalog lacks the model, so it was skipped), batch-only, no-embeddings (the gateway has no embeddings route, so /v1/embeddings skipped it), no-model (the gateway said 404), rate-limited (429), provider-auth (401 — fix that provider key), server-error (5xx), http-NNN for anything else. The same chain is drawn on the request's row in Logs. Why a 402 or 404 fails over, and what a rule can hold, is in Routing Rules → Why failover triggers and rule limits.

My first request returned 402

Two different things return 402. Tell them apart with code and X-FreeRouter-Attempts:

  • no_route, Attempts: 0 — the key is not routed to any provider. Open the key card and tick a provider under Route this key to.
  • all_targets_failed, Attempts ≥ 1 — the gateway relayed its own 402: the account behind your provider key has no credit. New gateway accounts usually start empty. Either add credit there, or make your first call with openrouter/free, which answers on any OpenRouter key.

The Anthropic SDK gets 401 or 404

  • 401 — the SDK sends the key as x-api-key, which FreeRouter accepts. Check that api_key (or ANTHROPIC_API_KEY) holds the fr_live_… key, not your Anthropic key.
  • 404 wrong_api_shape — the key's shape is openai. The Messages endpoint needs an anthropic-shaped key.
  • 404 not_found — the base URL has /v1 on it. The Anthropic SDK appends /v1/messages itself, so set base_url to https://api.freerouter.com with no path.

Working snippets for both SDKs and Claude Code are in Use your SDK.

Streaming stops after about 100 seconds

Cloudflare sits in front of api.freerouter.com and closes a connection that has carried no bytes for roughly 100 s (HTTP 524). FreeRouter sends nothing until the gateway's first byte, so the upstream timeout is capped at 90 s to stay under that limit. On the anthropic shape FreeRouter emits a ping event after 10 s of silence to keep long thinking alive; chat streams carry only what the gateway sends. If a model is silent for over 100 s on a chat stream, the connection drops — use a shorter prompt, a faster model, or the anthropic shape. A request that is still failing over at that point does not get there: the whole request is bounded by a 95 s deadline, so a rule with several slow targets answers 504 deadline_exceeded (with deadline in X-FreeRouter-Failover) instead of a Cloudflare 524 with no headers at all. Timeouts are per key: see Routing Rules → Timeouts.

It works in Playground but not from my app

Playground and the Test key button run from the dashboard and are never written to Logs, so a request from your app is the first one FreeRouter sees. Check, in order:

  1. Logs → filter Errors (400+). If the row is there, its explanation says what failed and links the fix.
  2. No row at all: the request never reached FreeRouter — wrong base URL (/v1 missing or doubled), a proxy, or DNS. Try the curl from the key card.
  3. Different IP: an IP allowlist admits your laptop but not the server. The 403 names the address.
  4. Different model id: Playground picks from the key's catalog; your app may send one no target lists (model_not_available).

Where to look in Logs

  • Logs open on the last 7 days. Set the status filter to Errors (400+).
  • A failed row carries a one-line explanation and a link to the fix (the provider, the key's routing, the allowlist). Rejections FreeRouter made before sending anything show provider None.
  • Expand a row for the attempt path: each gateway tried, its status, and the timing budget that cut it.
  • Playground runs and Test key never appear here. Details: Usage & Spend.
Next What's different from calling the gateway directly