Python 88.3%
TypeScript 7.6%
Shell 4.1%
1// Robust Anthropic error handling (TypeScript SDK): typed errors, retry matrix, request ids, streaming errors.2// STATUS: LIVE_VERIFIED 2026-09-18 (deliberate 404 + 400 on claude-haiku-4-5-20251001; nothing billed).3// Run: node --env-file=.env --experimental-strip-types examples/shared/errors/anthropic_error_handling.ts4import Anthropic from "@anthropic-ai/sdk";5import { appendFileSync } from "node:fs";67const client = new Anthropic({ maxRetries: 0, timeout: 60_000 }); // production: keep default maxRetries 2 (408/409/429/5xx + connection errors, honors retry-after)8const MODEL = "claude-haiku-4-5-20251001";9const sleep = (ms: number) => new Promise((r) => setTimeout(r, ms));10const log = (status: number, note: string) =>11 appendFileSync("reports/live-requests.jsonl", JSON.stringify({ ts: new Date().toISOString().replace(/\.\d+Z$/, "Z"), provider: "anthropic", method: "POST", path: "/v1/messages", status, est_cost_usd: 0, note }) + "\n");1213function isFatal(e: Anthropic.APIError): boolean {14 return e instanceof Anthropic.BadRequestError || e instanceof Anthropic.AuthenticationError || e instanceof Anthropic.PermissionDeniedError ||15 e instanceof Anthropic.NotFoundError || e instanceof Anthropic.UnprocessableEntityError;16}1718async function withBackoff<T>(fn: () => Promise<T>, attempts = 4, base = 500, cap = 8000): Promise<T> {19 for (let i = 0; ; i++) {20 try {21 return await fn();22 } catch (e) {23 if (!(e instanceof Anthropic.APIError)) throw e;24 const shouldRetry = e.headers?.get?.("x-should-retry"); // live: "false" on every 4xx25 const retryAfter = e.headers?.get?.("retry-after");26 if (isFatal(e) || shouldRetry === "false" || i === attempts - 1) throw e;27 const delay = retryAfter ? Number(retryAfter) * 1000 : Math.min(cap, base * 2 ** i) * (1 + Math.random() * 0.25);28 console.log(` retryable ${e.status} ${e.name} (request-id ${e.requestID}); sleeping ${Math.round(delay)}ms`);29 await sleep(delay);30 }31 }32}3334async function demo(label: string, fn: () => Promise<unknown>) {35 try {36 console.log(`${label}: ok ->`, await fn());37 } catch (e) {38 if (e instanceof Anthropic.APIError) {39 const err = (e.error as any)?.error ?? {};40 console.log(`${label}: HTTP ${e.status} ${e.constructor.name} type=${err.type} msg=${JSON.stringify(err.message)} request_id=${e.requestID}`);41 log(e.status ?? 0, `error-handling demo ${label}`);42 } else if (e instanceof Anthropic.APIConnectionError) {43 console.log(`${label}: connection problem`, e.name, e.message);44 } else throw e;45 }46}4748await demo("404 unknown model", () => client.messages.create({ model: "claude-does-not-exist", max_tokens: 8, messages: [{ role: "user", content: "Reply with OK." }] }));49await demo("400 bad temperature", () => client.messages.create({ model: MODEL, max_tokens: 8, temperature: 5, messages: [{ role: "user", content: "Reply with OK." }] }));5051// Streaming: `error` events after HTTP 200 surface as thrown APIError / 'error' event on MessageStream.52try {53 const stream = client.messages.stream({ model: MODEL, max_tokens: 8, messages: [{ role: "user", content: "Reply with OK." }] });54 stream.on("error", (err) => console.log("stream error event:", err.name));55 const final = await stream.finalMessage();56 console.log("stream ok:", final.content[0]?.type === "text" ? final.content[0].text.trim() : "", "| stop_reason:", final.stop_reason);57 log(200, "error-handling demo stream");58} catch (e) {59 console.log("stream failed:", (e as Error).name, (e as Error).message);60}61