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

Errors, Retries, and the Idempotency Story

~14 min · errors, retries, idempotency, 5xx

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

The error taxonomy

The Anthropic API uses standard HTTP error codes with structured JSON bodies. 400 is your bug (bad request shape). 401/403 is auth. 429 is rate limit. 500/503 is the server side; retry. 529 is overloaded; retry with backoff. The SDK raises typed exceptions matching these so you do not parse the body manually.

Retries that are safe vs retries that double-bill you

Non-streaming POSTs are safe to retry on 429/5xx — the SDK does this automatically up to max_retries (default 2). Streaming requests are NOT safely retryable mid-stream — partial output is already consumed. For long, expensive completions, design for resume rather than retry.

Idempotency keys

The Messages API supports idempotency keys via the Idempotency-Key request header. If a network blip makes you unsure whether a call landed, retry with the same key — the API will return the original response instead of charging you twice. Use idempotency keys for any expensive call where retry semantics matter.

Principle: Decide your retry posture per call type, not globally. Cheap haiku reads can retry 5x; expensive opus generations want idempotency keys.

Code

Catch-by-type instead of by status code·python
from anthropic import (
    Anthropic,
    BadRequestError,
    AuthenticationError,
    PermissionDeniedError,
    NotFoundError,
    UnprocessableEntityError,
    RateLimitError,
    InternalServerError,
    APIConnectionError,
)

client = Anthropic(max_retries=2)

try:
    resp = client.messages.create(
        model="claude-sonnet-4-6",
        max_tokens=512,
        messages=[{"role": "user", "content": "hi"}],
    )
except BadRequestError as e:
    raise  # your bug — fix the request shape
except (AuthenticationError, PermissionDeniedError):
    raise  # auth — alert ops
except RateLimitError as e:
    schedule_retry(after=e.response.headers.get("retry-after"))
except (InternalServerError, APIConnectionError):
    schedule_retry(after=2.0)  # transient — back off and retry
Idempotency key for an expensive call·python
import uuid

idem_key = f"order-{order_id}-claim-summary"  # deterministic per business operation

resp = client.messages.create(
    model="claude-opus-4-7",
    max_tokens=4096,
    messages=[{"role": "user", "content": claim_text}],
    extra_headers={"Idempotency-Key": idem_key},
)

External links

Exercise

Audit one entry point in your code that calls Claude. List the expected exception types it can raise and the action taken for each. Add the missing branches and an integration test that simulates each.
Hint
If your code only catches generic Exception, you have not really designed for failure — you have hidden 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.