Troubleshooting
Every error has an HTTP status and a fixed error.code.
{
"error": {
"code": "insufficient_quota",
"message": "This request was refused. The usual cause is insufficient credit — add credit from the billing page and retry.",
"type": "insufficient_quota"
}
}Error codes
| Status | error.code | What to do |
|---|---|---|
| 400 | invalid_request | Check the body is valid JSON. Remove parameters the model may not support, and send only one of max_tokens and max_completion_tokens. |
| 401 | invalid_api_key | Send Authorization: Bearer sk-… with no stray spaces. Check that the key isn't revoked. |
| 403 | insufficient_quota | Usually out of credit: buy credits, or lower max_tokens. |
| 404 | unknown_endpoint | Use the base URL with /v1. Only Chat Completions, models and billing are served. |
| 408 | request_timeout | Retry. For long outputs, stream. |
| 413 | request_too_large | Shorten the prompt or compact the conversation. |
| 429 | rate_limit_exceeded | Too many requests for your account. Send fewer at once, and wait for Retry-After when it's set. |
| 429 | plan_limit | A limit of your monthly plan is used up. Wait for Retry-After or resets_at, or turn on Use balance. |
| 500 | internal_error | Retry. If a request that used to work keeps failing, the model may not support it. |
| 502 | upstream_error | The provider failed. Retry shortly. |
| 503 | model_unavailable | Check the model ID against GET /models, then retry, or fall back to another model. |
| 504 | gateway_timeout | The provider was too slow. Retry. |
Back off on retries instead of retrying at once. A request that fails before any output is not charged. Before retrying a tool call, check whether the tool already ran.
x402 top-ups
Error codes for POST /api/x402/topup and POST /api/x402/plan (a monthly plan); see Pay with x402.
| Status | error.code | What to do |
|---|---|---|
| 401 | invalid_api_key | Send Authorization: Bearer sk-… with a caft API key. |
| 402 | payment_invalid | The payment signature is not valid. Request a new quote and pay again. |
| 402 | payment_expired | The authorization expired before settlement. Start over. |
| 402 | wrong_terms | The payment does not match the offer. Request a new quote. |
| 402 | payment_refused | The payment was refused. Nothing was charged. |
| 402 | payment_screened | The payer address failed compliance screening. Nothing was charged. |
| 409 | payment_replayed | This exact authorization was already used. Sign a fresh one. |
| 402 | insufficient_funds | The paying wallet does not hold enough USDC on this network. |
| 422 | invalid_amount | amount_usd must be $1–$500 with at most two decimals. |
| 422 | invalid_tier | /api/x402/plan: tier must be plan10, plan25 or plan50. |
| 409 | already_active | /api/x402/plan: you already have this plan. Nothing was charged. |
| 409 | tier_paused | /api/x402/plan: this plan is not on sale right now. Nothing was charged. |
| 409 | stripe_plan_active | /api/x402/plan: your plan is billed by card; change it in the dashboard. Nothing was charged. |
| 409 | downgrade_not_available | /api/x402/plan: a lower plan can only start when your current plan ends. Nothing was charged. |
| 409 | purchase_in_progress | /api/x402/plan: a plan purchase is still being processed. Try again in a minute. |
| 429 | rate_limit_exceeded | More than 30 top-up attempts in an hour. Wait for Retry-After. |
| 500 | settlement_unattributable | The settlement could not be attributed to your payment. Email support@caft.dev with the transaction hash. |
| 503 | chain_unavailable | Could not check the paying wallet's balance. Retry shortly. |
| 503 | facilitator_unavailable | The payment facilitator is unreachable. Try again shortly. |
| 503 | settlement_unknown | The settlement outcome is unknown. Check your balance before paying again. |
Wallet accounts
Error codes for POST /api/x402/register and POST /api/wallet/keys; see Wallet accounts. Payment refusals use the same codes as the x402 top-ups table.
| Status | error.code | What to do |
|---|---|---|
| 400 | invalid_request | Send { message, signature, action } as JSON; name must be 1–40 characters and revoke needs a numeric id. |
| 401 | siwe_invalid | The sign-in message is not valid. Check the domain is caft.dev, chainId 8453, the nonce is fresh, and the statement names the action (caft wallet keys: create, …: list, …: revoke <id>). |
| 401 | siwe_expired | The sign-in message expired. Sign a new one. |
| 401 | siwe_replayed | This sign-in message was already used. Sign a new one. |
| 401 | wallet_unknown | No account exists for this wallet. Register with POST /api/x402/register first. |
| 403 | account_disabled | This account is disabled. Contact support. |
| 404 | not_found | Wallet accounts are not enabled, or the key id is not on this account. |
| 409 | wallet_registered | This wallet already has an account. Get a key with POST /api/wallet/keys; add credit with POST /api/x402/topup. |
| 409 | key_limit | An account can hold at most 10 keys. Revoke one first. |
| 422 | invalid_amount | amount_usd must be $5–$500 with at most two decimals. |
| 429 | rate_limit_exceeded | More than 30 signed requests per hour per wallet, or 5 registrations per hour from one address. Wait for Retry-After. |
| 202 | registration_incomplete | The payment went through; the account is finished automatically within minutes. Then get a key with POST /api/wallet/keys. |
| 503 | chain_unavailable | Could not check the paying wallet's balance. Retry shortly. |
| 503 | registration_busy | Too many registrations are running right now. Wait for Retry-After. |
A stream stops early
A complete stream ends with data: [DONE]. Treat anything else as incomplete, even if text arrived. If it ends at max_tokens with an unfinished tool call, raise the limit.
Empty answers
Reasoning models spend tokens thinking before they answer. If the app's response limit is low, they can run out before writing anything. Raise it to 2,048 or more.
The app wants another API format
caft serves OpenAI Chat Completions. For the Responses API or Anthropic Messages, put a translating router in between, such as LiteLLM or Claude Code Router; see Coding agents. If the app has no base URL field, use a router.
Still stuck
Check the status page, then email support@caft.dev with the status, the time, the model and the first few characters of your key. Never send the whole key.