Error reference
What each FluxRouter error means and how to fix it: 401 auth, every 402 refusal code, 429 rate limits and 5xx upstream errors.
FluxRouter returns standard HTTP status codes with an OpenAI-compatible JSON error body. Use this page to map a code to a cause and a fix.
Quick reference
| Code | Meaning | Most common cause | Fix |
|---|---|---|---|
| 401 | Unauthorised | Missing or invalid key | Send your sk-... key as Authorization: Bearer |
| 402 | Payment required | A money check refused the request | Read the refusal code; see 402 refusals |
| 429 | Too many requests | Rate limited, or the Free token allowance is used up | Back off and retry, or read the state field |
| 5xx | Server error | Transient upstream or gateway error | Retry with backoff |
Every error body follows the OpenAI error shape:
{
"error": {
"message": "Incorrect API key provided.",
"type": "invalid_request_error",
"code": "invalid_api_key"
}
}
The Anthropic-compatible endpoint returns the Anthropic error shape instead, with a top-level type of error and an error object inside it. The status codes and meanings below apply to both.
Full status reference
Read the message field for the specific reason. The most common cases:
| Status | What it means | What to do | Retry? |
|---|---|---|---|
| 401 | Key missing, expired or malformed | Create a new key at /home/keys; send it as Authorization: Bearer (or x-api-key on the Anthropic path) | Yes, with a valid key |
| 402 | A money check refused the request | Branch on the refusal code, see 402 refusals | Depends on the code |
| 403 | Endpoint not served | Use a supported path; see the API reference | No |
| 429 | Rate limited | Back off and retry with exponential backoff; honour Retry-After | Yes |
| 429 | state: free_cap_reached | The Free plan's monthly token allowance is used up. Move to a paid plan or wait for the next month | Next month |
| 429 | This conversation hit its limit | Start a new conversation | New conversation |
| 400 | Bad request body (malformed JSON, wrong shape, stream not a boolean) | Fix the request body | No |
| 400 | Model not recognised | Use flux-auto, flux-fast, flux-standard, flux-reasoning, or a valid pinned id | No |
| 400 | Empty prompt | Add message content | No |
| 413 | Input too long for your plan's models | Trim the history or upgrade | No |
| 413 | Request payload too large (usually images) | Compress or remove attachments | No |
| 502 | Upstream provider error | Retry with exponential backoff | Yes |
| 502 | Post-call processing glitch (the call may have succeeded) | Retry; if it persists, contact support with the request id | Yes |
| 503 | Service busy or in maintenance | Back off and retry; check status.fluxrouter.ai | Yes |
Why am I getting a 401?
Symptom: Every request fails with 401 and an authentication error message.
Cause: The key is missing, malformed, or not a Flux key. This includes a leftover OpenAI or Anthropic key, a typo, or a missing Authorization header.
Fix:
Send your Flux key, which starts with
sk-, as a bearer token:curl https://api.fluxrouter.ai/v1/chat/completions \ -H "Authorization: Bearer sk-..." \ -H "Content-Type: application/json" \ -d '{"model":"flux-auto","messages":[{"role":"user","content":"ping"}]}'On the Anthropic path you can use
x-api-key: sk-...instead.Confirm there is no trailing whitespace or quote characters around the key.
If you rotated the key, update it everywhere your client reads it (environment variable, config file, secrets store).
402 refusals
An HTTP 402 (Payment Required) means the request was refused on a money check rather than served. It is a safety limit, not a bug, and it is recoverable. Lowering max_tokens or switching models will not clear it.
Every money refusal is HTTP 402 with a machine-readable code. Branch on the code, not on the message text, which may be reworded.
Where the code is
FluxRouter has two money gates and, today, they carry the code in two different places. A client that parses only one fails silently on the other.
| Family | What refuses | Where the code is |
|---|---|---|
| Balance and caps | your prepaid balance, your own daily or monthly cap, an unreconciled charge | the X-Flux-Hold-Reason response header (the body has only a plain sentence) |
| Account gates | your account spend ceiling, plan locks, the Free monthly cap, per-key caps, bring-your-own-key setup | the body, at error.provider_specific_fields.error |
Balance and caps example: status 402, header X-Flux-Hold-Reason: credit_exhausted, body:
{"error":{"message":"Your credit balance is exhausted. Top up to keep going. Top up at https://fluxrouter.ai/home/billing","type":"None","param":"None","code":"402"}}
Here code is the string "402": it restates the status. The reason is in the header.
Account gates example: status 402, no X-Flux-Hold-Reason header, body:
{"error":{"message":"account_monthly_budget_exhausted","type":"None","param":"None","code":"402","provider_specific_fields":{"error":"account_monthly_budget_exhausted","account_max_budget_usd":0.01,"spent_usd_this_month":0.0,"inflight_usd":0.1,"message":"This account's $0.01 monthly cap is exhausted. Upgrade your plan or wait until the next billing cycle.","upgrade_url":"https://fluxrouter.ai/home/billing"}}}
Read the header first and fall back to the body. This works for both families:
async function callFlux(body) {
const res = await fetch('https://api.fluxrouter.ai/v1/chat/completions', { /* ... */ });
if (res.status !== 402) return res;
const payload = await res.json();
const code =
res.headers.get('X-Flux-Hold-Reason') ??
payload?.error?.provider_specific_fields?.error ??
null;
switch (code) {
case 'credit_exhausted': return topUpBalance();
case 'daily_cap': return waitUntilUtcMidnight();
case 'monthly_cap': return raiseMonthlyCapOrWait();
case 'account_monthly_budget_exhausted': return requestHigherCeiling();
case 'free_tier_exhausted': return addAPaidPlan();
case 'credit_unresolved': return retryThenContactSupport();
default: return surfaceToOperator(code, payload);
}
}
Every refusal code
Four of these look alike and need four different fixes. An exhausted balance needs money. A reached daily cap needs time or a higher cap. A reached monthly cap needs a higher cap or the next month. A reached account ceiling needs a ceiling increase: a top-up will not clear it.
| Code | Carrier | What it means | What to do |
|---|---|---|---|
credit_exhausted | header | Your prepaid balance cannot cover this request. | Top up at /home/billing. |
daily_cap | header | Your own daily spend cap is reached. | Wait for midnight UTC, or raise the cap. |
monthly_cap | header | Your own monthly spend cap is reached. | Raise the cap, or wait for the next month. |
credit_unresolved | header | Your balance could not be read, or a prior charge is still being reconciled. | Retry once; if it persists, contact support. |
account_monthly_budget_exhausted | body | Your account spend ceiling is reached. | See spend caps. A top-up does not clear this. |
spend_ceiling_unresolved | body | No ceiling could be resolved for this key, so the request is refused rather than served uncapped. | Contact support. |
free_tier_exhausted | body | The Free plan's monthly credit is used up. | Add a paid plan, or wait for the monthly reset. |
premium_locked | body | The model needs an active paid plan with a cleared payment. | Add a plan, or wait for the first payment to clear. |
tier_locked | body | The model is above your account's current access level. | It unlocks on the date in the body's unlock_at. |
key_lifetime_cap_exhausted | body | This key's lifetime cap is reached. | Raise the cap on the key, or use another key. |
account_disputed | body | The account is on hold pending a payment review. | Contact support. |
byok_no_capable_provider | body | No provider you hold a key for can serve this model. | Add a key for a capable provider, or change model. |
byok_no_deployment | body | That model has no bring-your-own-key deployment. | Use a model that supports your own key, or a Flux-billed key. |
byok_no_account | body | Bring-your-own-key needs an account-scoped key. | Use an account-scoped key. |
byok_no_key | body | No active key on file for that provider. | Add the provider key. |
The same table, machine-readable. This block is the published contract: a test fails if it and the set of codes the gateway can emit ever differ. measured: true marks the refusals driven against the live API and captured on the wire; the rest come from the gateway's own refusal sites.
{"refusal_codes": [
{"code": "credit_exhausted", "http_status": 402, "family": "credit", "carrier": "header", "measured": true},
{"code": "credit_unresolved", "http_status": 402, "family": "credit", "carrier": "header", "measured": true},
{"code": "daily_cap", "http_status": 402, "family": "credit", "carrier": "header", "measured": true},
{"code": "monthly_cap", "http_status": 402, "family": "credit", "carrier": "header", "measured": true},
{"code": "account_monthly_budget_exhausted", "http_status": 402, "family": "gate", "carrier": "body", "measured": true},
{"code": "spend_ceiling_unresolved", "http_status": 402, "family": "gate", "carrier": "body", "measured": false},
{"code": "free_tier_exhausted", "http_status": 402, "family": "gate", "carrier": "body", "measured": false},
{"code": "premium_locked", "http_status": 402, "family": "gate", "carrier": "body", "measured": false},
{"code": "tier_locked", "http_status": 402, "family": "gate", "carrier": "body", "measured": false},
{"code": "key_lifetime_cap_exhausted", "http_status": 402, "family": "gate", "carrier": "body", "measured": false},
{"code": "account_disputed", "http_status": 402, "family": "gate", "carrier": "body", "measured": false},
{"code": "byok_no_capable_provider", "http_status": 402, "family": "gate", "carrier": "body", "measured": false},
{"code": "byok_no_deployment", "http_status": 402, "family": "gate", "carrier": "body", "measured": false},
{"code": "byok_no_account", "http_status": 402, "family": "gate", "carrier": "body", "measured": false},
{"code": "byok_no_key", "http_status": 402, "family": "gate", "carrier": "body", "measured": false}
]}
Requests already in flight at the cutoff
There is always a short gap between the moment a limit is reached and the moment the last request already in flight settles, so a client running many requests at once can end up slightly past the line. Any overshoot past your balance is ours. It is never carried forward, it never appears on a later invoice, and it never reduces a later top-up.
Fixing a 402
| If you received... | To resolve it |
|---|---|
free_tier_exhausted | Add a paid plan on the pricing page to keep going now, or wait for the monthly reset. Your key does not change. |
credit_exhausted | Top up your prepaid balance at /home/billing. |
daily_cap or monthly_cap | These are caps you set. Raise them in billing settings, or wait for the reset. |
account_monthly_budget_exhausted | Wait for the monthly reset, or ask for a higher ceiling. See spend caps. |
| Anything unexpected | Check /home/activity for a runaway loop or a leaked key. Rotate the key at /home/keys if you suspect a leak, then fix the source of the traffic. |
Monthly caps and ceilings reset each calendar month; daily caps reset at midnight UTC.
Why am I getting a 429?
Symptom: Bursts of requests fail with 429.
Cause: You are sending requests faster than your plan allows, or, on the Free plan, the monthly token allowance is used up. The second case carries "state": "free_cap_reached" in the error body and does not clear with a retry.
Fix:
- Back off and retry. Use exponential backoff (for example 1s, 2s, 4s) with jitter, and honour
Retry-After. - Reduce concurrency, or add a small delay between requests in tight loops.
- For
free_cap_reached, move to a paid plan or wait for the next month. See The Free plan. - See Rate limits and retries for patterns.
Why am I getting a 5xx?
Symptom: Occasional 500, 502, 503 or 504 responses.
Cause: A transient upstream model error or a gateway hiccup.
Fix:
- Retry the request with backoff. Most
5xxresponses are transient and succeed on retry. - If a specific request fails repeatedly, capture the
X-Flux-Request-Idfrom the response and contact support with that id. - Persistent
5xxon every request usually points to a malformed request body. Check that your JSON is valid and thatmessages(orinput) is well formed.
Ability errors
This page covers chat and model calls. The abilities (speech, transcription, images, search, page reading, apps) and the two MCP servers use a few other error shapes and codes:
- Error reference for every endpoint and tool: every shape and every ability code, with whether to retry.
- Fixes for each ability:
Including the request id
For any error you escalate, include the X-Flux-Request-Id response header and email support at fluxrouter dot ai. The id lets support find your exact request. See Receipts for how to read response headers.