Claude API Error: Common Codes and How to Fix Them
A Claude API error almost always falls into one of six buckets: bad request formatting, authentication failure, rate limiting, an overloaded model, a server-side fault, or a network timeout. The fastest way to fix it is to read the HTTP status code and the error.type field in the JSON response body — Anthropic's API returns structured error objects, not vague strings, so you rarely need to guess.
This article walks through each error type you'll actually run into, what causes it, and how to handle it in production code — including retry logic that won't make things worse.
The Anatomy of a Claude API Error Response
When a request fails, you get an HTTP status code plus a JSON body shaped like this:
{
"type": "error",
"error": {
"type": "invalid_request_error",
"message": "messages: at least one message is required"
}
}
Always parse error.type before deciding how to react. Retrying an invalid_request_error will fail every time — the payload itself is wrong. Retrying a rate_limit_error or overloaded_error after a delay usually works.
Common Error Codes and What They Mean
400 — invalid_request_error
The request body is malformed: missing model, empty messages array, max_tokens exceeding the model's limit, or an unsupported parameter combination (e.g. temperature and top_p both set aggressively, or using tools without a valid tool_choice).
Fix: validate your payload against the docs before sending. Check /docs/messages for the exact schema and required fields.
401 — authentication_error
The API key is missing, malformed, or revoked. This also fires if you send the key in the wrong header (it must be a proper Authorization or x-api-key header, not a query parameter).
Fix: confirm the key is loaded from environment variables correctly and hasn't been rotated. If you're using SubToAPI as your gateway, the same rule applies — your sub_live_... key goes in Authorization: Bearer $SUBTOAPI_KEY, and a 401 there almost always means the env var is empty at runtime, not that the key is wrong.
403 — permission_error
The key is valid but doesn't have access to the requested resource — commonly a model your account tier doesn't support, or a feature flag that isn't enabled.
Fix: check which models your account/plan can call. Downgrading to a supported model or requesting access usually resolves it.
404 — not_found_error
The endpoint or resource doesn't exist — often a typo in the URL path, or referencing a message/batch ID that was never created.
413 — request_too_large
The payload exceeds the maximum request size, usually from oversized attachments, huge conversation histories, or embedding large files directly in the message content.
Fix: trim conversation history, summarize old turns, or move large content to file references if your integration supports it.
429 — rate_limit_error
You've exceeded requests-per-minute, tokens-per-minute, or concurrent request limits for your tier. This is the most common error developers hit at scale.
Fix: implement exponential backoff and respect the retry-after header when present. Don't just fire retries in a tight loop — that compounds the problem across concurrent requests.
async function callWithBackoff(fn, maxRetries = 5) {
for (let attempt = 0; attempt < maxRetries; attempt++) {
try {
return await fn();
} catch (err) {
if (err.status !== 429 && err.status !== 529) throw err;
const delay = Math.min(1000 * 2 ** attempt, 30000);
await new Promise(r => setTimeout(r, delay));
}
}
throw new Error("Max retries exceeded");
}
500 — api_error
Something failed on Anthropic's end. This is rare and not caused by anything in your request.
Fix: retry with backoff. If it persists for more than a few minutes, check the provider's status page before assuming it's your code.
529 — overloaded_error
The model is temporarily overloaded with traffic. This is different from a rate limit — it's not about your usage, it's about total demand on that model at that moment.
Fix: same backoff strategy as 429. Some teams also fall back to a secondary model for a few requests if 529s spike, then switch back once the primary model recovers.
Errors That Aren't in the Response Body
Two categories of failure won't show up as a clean JSON error:
- Network timeouts — long streaming responses or slow connections can time out at the client or proxy level before the API itself responds. Set generous but bounded timeouts (60–120s for non-streaming calls) and use streaming for anything that might run long — see
/docs/streaming. - Silent tool-call mismatches — if you're using tool use and the model's
tool_callsoutput doesn't match your function schema, you won't get an API error at all; you'll get a malformed response your own code fails to parse. Validate tool schemas carefully —/docs/toolscovers the expected shapes.
Reducing Error Noise in Multi-App Setups
If you're running Claude across several internal services or apps, a lot of "Claude API errors" are actually key management problems: one service hits a rate limit and starves the others, or a key gets rotated and three apps break at once because nobody tracked where it was used.
SubToAPI addresses this by sitting between your apps and your Claude access: each app gets its own sub_live_... key, so a misbehaving service doesn't take down the others, and you get usage metadata per key to spot which one is triggering 429s before it becomes a full outage. It won't eliminate upstream 529s or 500s — those are model-side — but it isolates the blast radius of the errors you can control. Check /pricing or start with /signup if that's useful for your setup.
Building Resilient Error Handling
A production-grade pattern:
- Catch errors by HTTP status, not by string-matching messages.
- Retry only on 429, 500, 529, and network timeouts — never on 400, 401, 403.
- Use exponential backoff with jitter, capped at a sane maximum (30–60s).
- Log
error.typeanderror.messagefor every failure so you can distinguish "my payload is wrong" from "the model is overloaded" at a glance. - Set a circuit breaker: after N consecutive failures, stop retrying and alert instead of hammering the API.
Getting this right once, in a shared client wrapper, saves you from re-debugging the same error categories in every service that calls Claude.
Questions
Why do I get a 401 error even though my API key looks correct? Usually the key is being read from an empty or stale environment variable at runtime, or it's placed in the wrong header. Print the first few characters of the key at startup to confirm it's actually loaded.
Should I retry every Claude API error automatically? No. Retry 429, 500, 529, and timeouts with backoff. Never retry 400, 401, 403, or 404 — those need a code or configuration fix, not a resend.
What's the difference between a rate limit error and an overloaded error? A 429 rate limit error means you've exceeded your own account's usage limits. A 529 overloaded error means the model itself is under heavy total demand and applies regardless of your individual usage.