Python SDK Exception Types at a Glance
A dense reference for every exception class the anthropic Python SDK raises, and whether each one is safe to retry.
How to Use This Cheatsheet
- Look up the exception you caught (or want to catch) in the main table for its HTTP status and retry-safety.
- Use the "Catch order" section when writing an
exceptchain - most specific first. - Use the "What the SDK already retries" table before adding your own retry logic on top of the SDK's built-in retries.
Exception Classes at a Glance
| Exception | HTTP status | Retry-safe? | Typical cause |
|---|---|---|---|
BadRequestError | 400 | No | Malformed request, invalid parameter value |
AuthenticationError | 401 | No | Missing or invalid API key |
PermissionDeniedError | 403 | No | API key lacks access to the requested model or feature |
NotFoundError | 404 | No | Invalid model ID or endpoint |
UnprocessableEntityError | 422 | No | Request is well-formed JSON but semantically invalid |
RateLimitError | 429 | Yes (already auto-retried) | Exceeded requests-per-minute, tokens-per-minute, or tokens-per-day |
InternalServerError | 5xx | Yes (already auto-retried) | Transient Anthropic service issue |
APIConnectionError | (no response) | Yes (already auto-retried) | DNS failure, connection refused, network interruption before any response |
APIStatusError | any non-2xx | Depends - base class | Catch-all for any HTTP error not matched by a more specific subclass above |
APIError | (any) | Depends - base class | Root of the whole exception hierarchy; broadest possible catch |
Catch Order (Most Specific First)
import anthropic
try:
message = client.messages.create(
model="claude-sonnet-5",
max_tokens=1024,
messages=[{"role": "user", "content": "Hello"}],
)
except anthropic.NotFoundError:
# Bad model ID or endpoint - fix the request, do not retry
...
except anthropic.RateLimitError:
# Exhausted the SDK's own retries; back off longer or reduce request rate
...
except anthropic.APIStatusError as e:
# Any other non-2xx HTTP response
print(e.status_code, e.message)
except anthropic.APIConnectionError:
# Network failure before any response was received
...APIConnectionError is a sibling of APIStatusError in Python (not a subclass), so it needs its own except clause; it will not be caught by an except anthropic.APIStatusError branch.
What the SDK Already Retries Automatically
| Failure | Auto-retried by default? | Your code needs to... |
|---|---|---|
APIConnectionError (network failure) | Yes, up to max_retries (default 2) | Handle only if retries are exhausted |
429 (RateLimitError) | Yes, honoring retry-after | Handle only if retries are exhausted; consider a longer backoff |
5xx (InternalServerError) | Yes | Handle only if retries are exhausted |
408, 409 | Yes | Handle only if retries are exhausted |
400, 401, 403, 404, 422 | No | Fix the request or credentials; retrying unchanged fails again |
Retry-Safe Handling Strategies
| Exception | Recommended strategy |
|---|---|
RateLimitError (after retries exhausted) | Back off significantly longer than the SDK's default retry window, or shed load; consider a token-bucket rate limiter client-side |
APIConnectionError (after retries exhausted) | Surface a "service unreachable" state to the user; check your own network/DNS before assuming Anthropic is down |
InternalServerError (after retries exhausted) | Check status.anthropic.com; if persistent, treat as an outage rather than a code bug |
AuthenticationError | Never retry; verify ANTHROPIC_API_KEY is set and valid, then fail loudly - a silent retry loop against a bad key just wastes time |
BadRequestError | Never retry unchanged; log the request payload (redacting secrets) and fix the malformed field |
PermissionDeniedError | Never retry; the API key needs different permissions or a different model access grant |
NotFoundError | Never retry; almost always a typo'd model ID - check against the current model catalog |
Using .type for Finer-Grained Classification
Every APIStatusError subclass exposes a .type string matching the API's own error taxonomy, useful when a single HTTP status covers more than one underlying cause:
except anthropic.APIStatusError as e:
if e.type == "rate_limit_error":
...
elif e.type == "overloaded_error":
...
elif e.type == "invalid_request_error":
...FAQs
Do I need to write my own retry loop for RateLimitError?
Not for the common case.
The SDK already retries 429s automatically up to max_retries times with backoff; write your own handling only for what happens after retries are exhausted.
Why is APIConnectionError not a subclass of APIStatusError?
Because it represents a failure that happened before any HTTP response was received (a network error), whereas APIStatusError and its subclasses represent a response that came back with a non-2xx status. They need separate except clauses.
What's the difference between catching APIStatusError and APIError?
APIError is the root of the entire hierarchy, including APIConnectionError. APIStatusError covers only responses that actually reached you with an HTTP status code. Catching APIStatusError specifically excludes connection-level failures.
Should AuthenticationError ever be retried?
No.
A bad or missing API key does not become valid by retrying; fix the credential and fail fast instead of looping.
What does UnprocessableEntityError mean in practice?
The request was valid JSON with the right shape, but the API rejected its contents for a semantic reason - for example, a budget_tokens value that doesn't satisfy the API's constraints for that field.
How do I tell rate_limit_error apart from overloaded_error if both could show up as different exceptions?
RateLimitError maps to HTTP 429 specifically, while overloaded_error (a 529) is typically caught as APIStatusError - check e.type == "overloaded_error" inside that branch to distinguish it.
What's the most common mistake when catching these exceptions?
Writing a single broad except anthropic.APIError: (or catching plain Exception) that treats every failure identically, losing the distinction between retryable and non-retryable errors. Catch the most specific class you care about first.
Does the async client (AsyncAnthropic) raise the same exception types?
Yes.
The exception hierarchy is identical between Anthropic() and AsyncAnthropic() - only the calling convention (await) differs.
Is a 404 always a bad model ID?
It's the most common cause, but any invalid endpoint path (unlikely if you're only using the SDK's own methods) would also produce a 404. Double-check the exact model ID string against the current catalog first.
What should I log when I catch an APIStatusError?
At minimum, e.status_code, e.message, and e.type. Avoid logging the full request body unredacted if it could contain sensitive user content.
Related
- Python SDK Basics - a first look at catching typed exceptions
- Python SDK Retry and Timeout Configuration Reference - what
max_retriesactually retries before these exceptions surface - The Anthropic Python SDK Mental Model - how retries and errors fit into the SDK's overall design
- Python SDK Best Practices - error handling as part of a broader checklist
Stack versions: Written against the Claude model lineup current as of ~June 2026 - Claude Fable 5, Claude Opus 4.8, Claude Sonnet 5 (the default), and Claude Haiku 4.5 - and the official
anthropicPython SDK (latest 0.x release). Model names, SDK versions, and pricing move quickly - verify current specifics at platform.claude.com/docs before relying on them.