TypeScript SDK Error Class Reference
The @anthropic-ai/sdk package throws a distinct error subclass for each HTTP status the Messages API can return, so you can write typed catch blocks instead of parsing status codes by hand. This reference compares every error class and shows a working most-specific-first catch chain.
How to Use This Cheatsheet
- Scan the table below to find the error class for a given HTTP status or symptom.
- Use the "Retryable" column to decide whether to let the SDK's built-in retry handle it or surface it immediately.
- Copy the catch chain example and adapt the branches you actually need, most apps don't need all of them.
- Keep
instanceof Anthropic.APIConnectionErrorordered beforeinstanceof Anthropic.APIErrorin any chain you write, since the former is a subclass of the latter.
Error Class Reference
| Class | HTTP Status | Retryable | Typical Cause |
|---|---|---|---|
Anthropic.BadRequestError | 400 | No | Malformed request body, invalid parameter value, or an unsupported combination of options. |
Anthropic.AuthenticationError | 401 | No | Missing, invalid, or expired API key. |
Anthropic.PermissionDeniedError | 403 | No | API key lacks access to the requested model or resource. |
Anthropic.NotFoundError | 404 | No | Referenced resource (e.g. a model ID or file) doesn't exist. |
Anthropic.UnprocessableEntityError | 422 | No | Request is well-formed JSON but fails semantic validation. |
Anthropic.RateLimitError | 429 | Yes | Too many requests or tokens for the current rate limit tier. |
Anthropic.InternalServerError | >=500 | Yes | Transient failure on Anthropic's side. |
Anthropic.APIConnectionError | (none, no response received) | Yes | Network failure, DNS error, or timeout before a response arrives. |
Anthropic.APIError | (base class) | Depends | Catch-all parent; matches only if no more specific subclass matched first. |
Class Hierarchy Note
Anthropic.APIConnectionError is a subclass of Anthropic.APIError in the TypeScript SDK, unlike Python where APIConnectionError is a sibling class. This means an instanceof Anthropic.APIError check will also match a connection error. Always check APIConnectionError (and any other specific subclass) before the generic APIError check in an if/else if chain, or the specific branch will never run.
Most-Specific-First Catch Chain
import Anthropic from "@anthropic-ai/sdk";
const client = new Anthropic();
async function askClaude(prompt: string) {
try {
return await client.messages.create({
model: "claude-sonnet-5",
max_tokens: 1024,
messages: [{ role: "user", content: prompt }],
});
} catch (error) {
if (error instanceof Anthropic.RateLimitError) {
// Retryable: the SDK already retried internally (default maxRetries: 2).
// If it still failed, back off longer or queue the request.
console.error(`Rate limited (type: ${error.type}). Try again later.`);
} else if (error instanceof Anthropic.AuthenticationError) {
// Non-retryable: fix the API key, don't retry.
console.error(`Auth failed (type: ${error.type}). Check ANTHROPIC_API_KEY.`);
} else if (error instanceof Anthropic.BadRequestError) {
// Non-retryable: fix the request payload.
console.error(`Bad request (type: ${error.type}): ${error.message}`);
} else if (error instanceof Anthropic.InternalServerError) {
// Retryable: transient server-side failure.
console.error(`Server error (type: ${error.type}). Safe to retry.`);
} else if (error instanceof Anthropic.APIConnectionError) {
// Retryable: check this BEFORE the base APIError check below,
// since APIConnectionError is a subclass of APIError.
console.error("Network error, no response was received.");
} else if (error instanceof Anthropic.APIError) {
// Catch-all for any other typed API error (status, type available).
console.error(`API error ${error.status} (type: ${error.type}): ${error.message}`);
} else {
// Not an SDK error at all (a bug in calling code, for example).
throw error;
}
}
}Retryable vs Non-Retryable at a Glance
| Category | Classes | Recommended Handling |
|---|---|---|
| Retryable | RateLimitError, InternalServerError, APIConnectionError | Let the SDK's built-in exponential backoff handle it (default maxRetries: 2); log and surface only if it still fails after retries. |
| Non-retryable | BadRequestError, AuthenticationError, PermissionDeniedError, NotFoundError, UnprocessableEntityError | Fail fast, surface the .message and .type to the caller, and fix the request or credentials rather than retrying. |
The .type Property
Every SDK error also exposes a .type property with the API's error type string, which gives finer-grained classification than the HTTP status code alone. Two errors can share a status but have a different .type:
try {
await client.messages.create({ /* ... */ });
} catch (error) {
if (error instanceof Anthropic.APIError) {
switch (error.type) {
case "invalid_request_error":
// maps to BadRequestError (400)
break;
case "authentication_error":
// maps to AuthenticationError (401)
break;
case "rate_limit_error":
// maps to RateLimitError (429)
break;
case "overloaded_error":
// can arrive as a 429 or 5xx depending on cause
break;
}
}
}FAQs
Why does APIConnectionError need to be checked before APIError?
Because in the TypeScript SDK APIConnectionError is a subclass of APIError, not a sibling. An instanceof Anthropic.APIError check matches connection errors too, so if that check comes first in an if/else if chain, the more specific APIConnectionError branch never executes.
Is this the same hierarchy as the Python SDK?
No. In the Python SDK, APIConnectionError is a sibling of APIError, not a subclass. Code ported from Python to TypeScript that assumes the same relationship will silently skip the connection-error branch.
Do I need to manually retry RateLimitError or InternalServerError?
Not usually. The SDK already auto-retries 429, 5xx, and connection errors with exponential backoff, using a default maxRetries of 2. Only add your own retry logic if you need a different retry count, backoff strategy, or queuing behavior.
Which errors should never be retried?
BadRequestError (400), AuthenticationError (401), PermissionDeniedError (403), NotFoundError (404), and UnprocessableEntityError (422). Retrying these wastes a request since the payload or credentials, not the network or server load, caused the failure.
What does the base APIError class catch?
Any typed SDK error that didn't match a more specific instanceof check earlier in the chain, since every specific error class extends APIError. Put it last in a catch chain as a safety net, not as your primary handling path.
How is `.type` different from `.status`?
.status is the HTTP status code (a number, like 429). .type is the API's own error type string (like "rate_limit_error" or "overloaded_error"), which lets you distinguish causes that share the same status code.
Can `overloaded_error` show up at a status other than 429?
Yes, overloaded_error can arrive under a 429 or a 5xx status depending on the cause, which is exactly the case where checking .type in addition to the status-mapped class gives you more precise information.
What triggers APIConnectionError specifically?
Anything that prevents a response from arriving at all, such as a DNS failure, a dropped connection, or a client-side timeout. It's distinct from the status-code errors because no HTTP response was ever received.
Should I catch errors per-call or with a shared wrapper?
For a small app, a shared wrapper function (like askClaude above) that centralizes the catch chain keeps handling consistent. For larger apps, wrap the catch chain in a utility so every call site gets the same retryable/non-retryable classification without duplicating the if/else if chain.
Does throwing the error back up matter in the final else branch?
Yes. The final else in the example re-throws anything that isn't an SDK APIError at all, such as a bug in your own calling code. Swallowing non-SDK errors silently makes real bugs harder to find.
Is UnprocessableEntityError common in practice?
Less common than BadRequestError, since it means the request was syntactically valid JSON but failed a semantic check, such as a value that's the right type but out of an allowed range.
What model name should I use in the example code?
The example uses claude-sonnet-5, the default model in the current Claude lineup (Claude Fable 5, Claude Opus 4.8, Claude Sonnet 5, Claude Haiku 4.5). Swap in whichever model your application targets.
Related
- TypeScript/JavaScript SDK Basics - install the SDK and make your first typed call before handling errors.
- Retry, Timeout, and AbortController Patterns in the TypeScript SDK - tune the SDK's built-in retry and cancellation behavior referenced in this page.
- The Anthropic TypeScript SDK Mental Model - how the SDK's client, requests, and typed responses fit together.
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
@anthropic-ai/sdkTypeScript SDK (latest release). Model names, SDK versions, and pricing move quickly - verify current specifics at platform.claude.com/docs before relying on them.