C.W.K.
Stream
Lesson 04 of 06 · published

Typed Errors and Retry Posture

~12 min · errors, retries, discriminated

Level 0Observer
0 XP0/64 lessons0/13 achievements
0/150 XP to next level150 XP to go0% complete

Typed error classes

The SDK exports concrete error classes mirroring Python: BadRequestError, AuthenticationError, PermissionDeniedError, NotFoundError, RateLimitError, InternalServerError, APIConnectionError. instanceof branches let you write retry posture without parsing status codes.

maxRetries on the constructor

Pass maxRetries when constructing the client to control automatic retry behavior on 429/5xx for non-streaming calls. Default is 2. Tighter for fast-feedback CLIs; looser for long-running batch-style scripts.

Idempotency in TypeScript

Idempotency keys are passed via headers: { 'Idempotency-Key': key } in the per-call options. Use them for any expensive Opus-class generation where you want to retry safely after a network blip without double-billing.

Principle: Discriminated error classes turn HTTP status code branching into compiler-checked branches. Use them.

Code

instanceof branches for retry posture·typescript
import Anthropic, {
  BadRequestError,
  AuthenticationError,
  RateLimitError,
  InternalServerError,
  APIConnectionError,
} from '@anthropic-ai/sdk';

const client = new Anthropic({ maxRetries: 2 });

async function callOnce() {
  try {
    return await client.messages.create({
      model: 'claude-sonnet-4-6',
      max_tokens: 256,
      messages: [{ role: 'user', content: 'hi' }],
    });
  } catch (e) {
    if (e instanceof BadRequestError) throw e;             // your bug
    if (e instanceof AuthenticationError) throw e;          // ops alert
    if (e instanceof RateLimitError) {
      const after = e.headers?.get('retry-after') ?? '2';
      await new Promise(r => setTimeout(r, Number(after) * 1000));
      return callOnce();
    }
    if (e instanceof InternalServerError || e instanceof APIConnectionError) {
      await new Promise(r => setTimeout(r, 2000));
      return callOnce();
    }
    throw e;
  }
}
Idempotency key for an expensive call·typescript
const idempotencyKey = `claim-${claimId}-summary`;

const resp = await client.messages.create(
  {
    model: 'claude-opus-4-7',
    max_tokens: 4096,
    messages: [{ role: 'user', content: claimText }],
  },
  { headers: { 'Idempotency-Key': idempotencyKey } },
);

External links

Exercise

In an existing TypeScript project, replace status-code-based error branching with instanceof checks against the SDK's exported classes. Add a unit test that mocks each error class and asserts the right retry posture.
Hint
If your tests start failing because BadRequestError now bubbles up, that is the right behavior — fix the request shape rather than swallowing it.

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue

Comments 0

🔔 Reply notifications (sign in)
Sign inPlease sign in to comment.

No comments yet — be the first.