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

CodeMeaningMost common causeFix
401UnauthorisedMissing or invalid keySend your sk-... key as Authorization: Bearer
402Payment requiredA money check refused the requestRead the refusal code; see 402 refusals
429Too many requestsRate limited, or the Free token allowance is used upBack off and retry, or read the state field
5xxServer errorTransient upstream or gateway errorRetry with backoff

Every error body follows the OpenAI error shape:

json
{
  "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:

StatusWhat it meansWhat to doRetry?
401Key missing, expired or malformedCreate a new key at /home/keys; send it as Authorization: Bearer (or x-api-key on the Anthropic path)Yes, with a valid key
402A money check refused the requestBranch on the refusal code, see 402 refusalsDepends on the code
403Endpoint not servedUse a supported path; see the API referenceNo
429Rate limitedBack off and retry with exponential backoff; honour Retry-AfterYes
429state: free_cap_reachedThe Free plan's monthly token allowance is used up. Move to a paid plan or wait for the next monthNext month
429This conversation hit its limitStart a new conversationNew conversation
400Bad request body (malformed JSON, wrong shape, stream not a boolean)Fix the request bodyNo
400Model not recognisedUse flux-auto, flux-fast, flux-standard, flux-reasoning, or a valid pinned idNo
400Empty promptAdd message contentNo
413Input too long for your plan's modelsTrim the history or upgradeNo
413Request payload too large (usually images)Compress or remove attachmentsNo
502Upstream provider errorRetry with exponential backoffYes
502Post-call processing glitch (the call may have succeeded)Retry; if it persists, contact support with the request idYes
503Service busy or in maintenanceBack off and retry; check status.fluxrouter.aiYes

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:

    bash
    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.

FamilyWhat refusesWhere the code is
Balance and capsyour prepaid balance, your own daily or monthly cap, an unreconciled chargethe X-Flux-Hold-Reason response header (the body has only a plain sentence)
Account gatesyour account spend ceiling, plan locks, the Free monthly cap, per-key caps, bring-your-own-key setupthe body, at error.provider_specific_fields.error

Balance and caps example: status 402, header X-Flux-Hold-Reason: credit_exhausted, body:

json
{"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:

json
{"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:

js
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.

CodeCarrierWhat it meansWhat to do
credit_exhaustedheaderYour prepaid balance cannot cover this request.Top up at /home/billing.
daily_capheaderYour own daily spend cap is reached.Wait for midnight UTC, or raise the cap.
monthly_capheaderYour own monthly spend cap is reached.Raise the cap, or wait for the next month.
credit_unresolvedheaderYour balance could not be read, or a prior charge is still being reconciled.Retry once; if it persists, contact support.
account_monthly_budget_exhaustedbodyYour account spend ceiling is reached.See spend caps. A top-up does not clear this.
spend_ceiling_unresolvedbodyNo ceiling could be resolved for this key, so the request is refused rather than served uncapped.Contact support.
free_tier_exhaustedbodyThe Free plan's monthly credit is used up.Add a paid plan, or wait for the monthly reset.
premium_lockedbodyThe model needs an active paid plan with a cleared payment.Add a plan, or wait for the first payment to clear.
tier_lockedbodyThe model is above your account's current access level.It unlocks on the date in the body's unlock_at.
key_lifetime_cap_exhaustedbodyThis key's lifetime cap is reached.Raise the cap on the key, or use another key.
account_disputedbodyThe account is on hold pending a payment review.Contact support.
byok_no_capable_providerbodyNo provider you hold a key for can serve this model.Add a key for a capable provider, or change model.
byok_no_deploymentbodyThat model has no bring-your-own-key deployment.Use a model that supports your own key, or a Flux-billed key.
byok_no_accountbodyBring-your-own-key needs an account-scoped key.Use an account-scoped key.
byok_no_keybodyNo 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.

json
{"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_exhaustedAdd a paid plan on the pricing page to keep going now, or wait for the monthly reset. Your key does not change.
credit_exhaustedTop up your prepaid balance at /home/billing.
daily_cap or monthly_capThese are caps you set. Raise them in billing settings, or wait for the reset.
account_monthly_budget_exhaustedWait for the monthly reset, or ask for a higher ceiling. See spend caps.
Anything unexpectedCheck /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 5xx responses are transient and succeed on retry.
  • If a specific request fails repeatedly, capture the X-Flux-Request-Id from the response and contact support with that id.
  • Persistent 5xx on every request usually points to a malformed request body. Check that your JSON is valid and that messages (or input) 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:

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.