Errors
Errors use Anthropic's exact wire format, so the error handling in your existing SDK integration keeps working unchanged.
Error shape
{
"type": "error",
"error": {
"type": "rate_limit_error",
"message": "Rate limit reached: 120 requests per minute. Retry in 24s."
}
}
The HTTP status and the error.type always agree. Messages are written to be
shown to a developer — they say what went wrong and what to do — so it is safe to
surface them in your own logs.
Error types
| Type | HTTP | Cause | What to do |
|---|---|---|---|
authentication_error |
401 | Missing, malformed, revoked or expired API key. | Check the key is being sent and has not been revoked. Not retryable. |
permission_error |
403 | The key or account is not allowed to do this: a model outside your plan, an IP outside the allow-list, or a suspended account. | The message says which. Not retryable without a change. |
not_found_error |
404 | Unknown model or endpoint. | Call GET /v1/models for the list you can use. |
invalid_request_error |
400 | The body failed validation — a missing field, a bad type, or a parameter the upstream rejected. | Fix the request. Not retryable. |
request_too_large |
413 | The body exceeded the size limit. | Split the request or reduce attachments. |
rate_limit_error |
429 | Too many requests per minute, or too many concurrent in-flight requests. | Back off and retry. Honour Retry-After. |
quota_exceeded |
429 | The monthly token allowance is exhausted, or the hard overage ceiling was reached. | Retrying will not help. Upgrade the plan or wait for the next billing period. |
residency_unavailable |
422 | No upstream can serve this request within your data-residency tier. | Choose a model available on your tier, or move to a wider tier. Never retry against a different region. |
overloaded_error |
503 | The upstream is saturated, or a credential we depend on is not configured. | Retry with exponential jitter. |
api_error |
500 | Something failed on our side or on the upstream. | Retry once, then contact support quoting the x-request-id. |
residency_unavailable is deliberately its own type rather than a generic
422. It must never be mistaken for a transient failure and retried into a different
region — that would be exactly the outcome your residency tier exists to prevent.
Errors during a stream
Once a streamed response has started, the HTTP status is already sent and cannot
change. A failure part-way through therefore arrives as an SSE error
event, which is what the Anthropic SDKs expect.
event: content_block_delta
data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"Once the"}}
event: error
data: {"type":"error","error":{"type":"api_error","message":"The upstream stream failed."}}
If you are parsing the stream yourself, treat an error event as terminal
and stop reading.
Retrying safely
import time, random, anthropic
client = anthropic.Anthropic(api_key=KEY, base_url="{BASE_URL_ROOT}")
def call(**kw):
for attempt in range(5):
try:
return client.messages.create(**kw)
except anthropic.APIStatusError as e:
kind = (e.body or {}).get("error", {}).get("type")
# Never retry these: the answer will not change, and for
# residency_unavailable a retry could only succeed by
# breaking the guarantee you are paying for.
if kind in {"authentication_error", "permission_error",
"invalid_request_error", "not_found_error",
"quota_exceeded", "residency_unavailable"}:
raise
wait = min(2 ** attempt + random.random(), 30)
time.sleep(float(e.response.headers.get("retry-after", wait)))
raise RuntimeError("exhausted retries")
Reporting a problem
Every response carries an x-request-id. Quote it when contacting support
and we can find the request in our logs immediately. It identifies the request
without revealing anything about its contents — we do not store prompt or completion
bodies.