Claude API Error Code Reference Guide
When a Claude API call fails, the response body tells you exactly what went wrong — but the meaning of invalid_request_error versus overloaded_error versus a bare 429 isn't always obvious if you haven't memorized the spec. This guide is a practical reference: every error type you'll encounter, what triggers it, and how to handle it in code.
The short version: Claude API errors follow a consistent JSON shape with an HTTP status code and a type field nested under error. Once you know the eight or nine error types that actually occur, debugging becomes a lookup problem instead of a guessing game.
The standard error shape
Every failed request returns something like this:
{
"type": "error",
"error": {
"type": "invalid_request_error",
"message": "messages.0.content: Field required"
}
}
The HTTP status code tells you the category, and error.type tells you the specifics. Always parse both — status codes alone aren't granular enough to decide retry logic.
Full error code reference
400 — invalid_request_error
The request is malformed: missing required fields, wrong types, an invalid model name, or a messages array that doesn't alternate user/assistant roles correctly. The message field usually points to the exact field. This is a client bug — retrying without fixing the payload will fail again every time.
Common causes:
- Missing
max_tokens - Two consecutive messages from the same role
- Invalid
modelstring (typo, deprecated model) - Malformed tool schema in a
toolsarray
401 — authentication_error
The API key is missing, malformed, or revoked. Check that the Authorization header (or x-api-key, depending on the platform you're using) is actually being sent and that the key hasn't been rotated. This is not something you retry — you need to fix credentials first.
403 — permission_error
The key is valid but doesn't have access to the requested resource — often a model tier restriction or an organization-level permission issue. Different from 401: authentication succeeded, authorization failed.
404 — not_found_error
The endpoint or resource doesn't exist. Usually a typo in the URL path, or you're hitting a resource ID (like a message or batch) that was deleted or never existed.
413 — request_too_large
The request payload exceeds size limits — typically from oversized file uploads, huge conversation histories, or large tool outputs stuffed into a single message. Trim the payload or paginate the conversation.
429 — rate_limit_error
You've exceeded requests-per-minute, tokens-per-minute, or a concurrency limit. This is retryable — back off and try again. Anthropic's official docs recommend exponential backoff with jitter; most SDKs implement this automatically. Watch for the retry-after header when present.
500 — api_error
An unexpected error on the provider's infrastructure. Rare, not your fault, and safe to retry after a short delay.
529 — overloaded_error
The API is temporarily overloaded and can't accept more traffic. This looks similar to a rate limit but is a capacity issue, not a quota issue. Retry with backoff; it usually clears within seconds to a couple of minutes.
Handling errors in code
Here's a general pattern that covers retryable vs. non-retryable errors:
async function callClaude(payload, retries = 3) {
for (let attempt = 0; attempt <= retries; attempt++) {
const res = await fetch("https://api.anthropic.com/v1/messages", {
method: "POST",
headers: {
"content-type": "application/json",
"x-api-key": process.env.ANTHROPIC_API_KEY,
"anthropic-version": "2023-06-01"
},
body: JSON.stringify(payload)
});
if (res.ok) return res.json();
const body = await res.json();
const type = body.error?.type;
const retryable = [429, 500, 529].includes(res.status);
if (!retryable || attempt === retries) {
throw new Error(`${type}: ${body.error?.message}`);
}
const delay = 2 ** attempt * 500 + Math.random() * 250;
await new Promise(r => setTimeout(r, delay));
}
}
The key decision point is retryable: 400, 401, 403, and 404 mean something is structurally wrong with your request or credentials, and retrying identical payloads wastes time and quota. 429, 500, and 529 are transient — retry with backoff.
Error codes get more confusing with proxies
If you're calling Claude through a proxy, gateway, or wrapper service, error codes can get muddied — you might see a generic 502 from the proxy layer instead of the underlying Claude error, which makes debugging harder because you've lost the error.type detail.
This is one of the reasons SubToAPI (https://subtoapi.app) passes through the same error shape and status codes you'd get from a direct integration, rather than wrapping or obscuring them. If you're using SubToAPI to turn your Claude access into an HTTPS API with application keys (sub_live_...), streaming, and usage metadata, error handling code you write against the reference above works unchanged. The docs cover the full request/response format, and the quickstart walks through your first authenticated call if you want to see the error responses firsthand.
Practical debugging checklist
When a call fails, work through this in order:
- Log the full error body, not just the status code —
error.typeandmessagetogether tell you almost everything. - Check the request payload against the messages format if you get a 400 — role alternation and required fields are the most common culprits.
- Verify the key is current and scoped correctly if you get a 401 or 403.
- Add backoff for 429 and 529 — don't hammer retries with fixed delays.
- If streaming, errors can arrive mid-stream as an event rather than an HTTP status — check the streaming docs for the event format.
- If using tools, malformed tool schemas often surface as 400 errors with a message pointing at the specific tool definition — see the tools docs for the expected structure.
questions
What does a 529 error mean for Claude API? It means the API is temporarily overloaded, not that you've hit a quota. It's retryable — use exponential backoff and the request will usually succeed within a short window.
How do I tell a rate limit error from an authentication error? Check the HTTP status: 401 means bad or missing credentials, 429 means you've exceeded a rate limit. The error.type field (authentication_error vs rate_limit_error) confirms it explicitly.
Should I retry every failed Claude API request? No. Only retry 429, 500, and 529 — these are transient. 400, 401, 403, and 404 indicate a problem with the request or credentials that retrying won't fix.