Claude Media
Claude APIのエラーハンドリング設計 — コード別対処とリトライ実装

Claude APIのエラーハンドリング設計 — コード別対処とリトライ実装

Claude APIが返す11種類のエラーをコード別に分類し、リトライ設計・SDKの型付き例外・ストリーミング中の回復・Batch APIの結果処理までをまとめます。

Claude APIが返すエラーは、400番台と500番台をあわせて11種類のHTTPステータスコードに集約されています。数だけ見れば少なく感じますが、実装側の対応は一様ではありません。コードを直せば消えるエラーと、待てば消えるエラーが混在するからです。この違いを設計に落とし込めていない実装は、直しても直らない400エラーを機械的に再送し続けたり、逆に数十秒で消える529を1回で見捨てたりします。

本記事では、ステータスコード別の分類、SDKの型付き例外によるハンドリング、ストリーミング中に届くエラーイベントの回復、Batch APIでのエラーの扱いまでを、公式仕様に沿ってまとめます。

エラーはどう返ってくるか — レスポンス形状とrequest_id

Claude APIはエラーを常にJSONで返します。トップレベルにerrorオブジェクトがあり、typemessageを必ず含みます。HTTPステータスコードとerror.typeは1対1で対応するため、両方を見れば原因を一意に判定できます。バージョニングポリシー上typeの値は今後増える可能性があり、未知の値が来た場合の既定処理を用意しておかないと、新しいエラー種別の追加に実装が追随できません。

{
  "type": "error",
  "error": {
    "type": "not_found_error",
    "message": "The requested resource could not be found."
  },
  "request_id": "req_011CSHoEeqs5C35K2UUqR7Fy"
}

レスポンスにはrequest_idフィールドが入り、同じ値がレスポンスヘッダーのrequest-idにも入ります。サポートへ問い合わせる際、この値が実質的に唯一の手がかりになります。エラーログにはerror.messageだけでなくrequest_idを必ず一緒に残しておくと、後から原因を追いやすくなります。

Claude Platform on AWS経由の場合はIDが2つ返ります。AWSのリクエストID(x-amzn-requestid)がCloudTrailの照合に使う主IDで、Anthropic側のリクエストIDは副IDです。Anthropicサポートへの問い合わせにはAnthropic側のIDを使います。マルチクラウド展開でログ基盤を統一している場合、2種類のIDをどちらもインデックスしておかないと片方の照会経路でしか原因調査ができません。

curl -sS -D - -o /dev/null https://api.anthropic.com/v1/messages \
  -H "x-api-key: $ANTHROPIC_API_KEY" \
  -H "anthropic-version: 2023-06-01" \
  -H "content-type: application/json" \
  -d '{"model":"claude-sonnet-5","max_tokens":1024,"messages":[{"role":"user","content":"Hello, Claude"}]}'

-D -でヘッダーだけを出力すれば、ボディを待たずにrequest-idを確認できます。障害調査の初動で使う小さなコマンドです。

ステータスコード別の原因と対処 — 11種類の分類

Claude APIのエラーは11種類のステータスコードに整理されています。原因を確認する起点になるのはこの一覧です。

コードエラータイプ主な原因
400エラータイプinvalid_request_error主な原因リクエストの形式・内容の不備
401エラータイプauthentication_error主な原因APIキーの失効・形式不正・AWS SigV4署名の問題
402エラータイプbilling_error主な原因支払い情報の不備
403エラータイプpermission_error主な原因対象リソースへのアクセス権限なし
404エラータイプnot_found_error主な原因指定したリソースが存在しない
409エラータイプconflict_error主な原因リソースの状態競合(同時更新・一意制約違反)
413エラータイプrequest_too_large主な原因リクエストサイズの上限超過
429エラータイプrate_limit_error主な原因アカウントがレート制限に到達
500エラータイプapi_error主な原因Anthropic側の内部エラー
504エラータイプtimeout_error主な原因処理中のタイムアウト
529エラータイプoverloaded_error主な原因APIの一時的な過負荷

この11種類は、対処の仕方で2つに分かれます。リクエスト側を直さない限り再送しても同じ結果になるものと、時間をおけば通ることがあるものです。400・401・402・403・404・409・413が前者、429・500・504・529が後者に当たります。前者をリトライループに入れると、無駄なリクエストを送り続けるだけでなく、レート制限の消費まで早めてしまいます。

409のconflict_errorは例外的です。対象リソースの状態が変わったあとに再取得すれば通ることがあります。純粋な再送ではなく、状態を取り直してからの再試行が前提です。

400のinvalid_request_errorは一枚岩ではありません。公式ドキュメントの「よくあるバリデーションエラー」には5パターンが列挙されています。リクエスト形式そのものの不備に加え、直前のassistantメッセージに含まれるthinkingブロックやredacted_thinkingブロックを編集・並べ替え・削除してから送り返した場合も400です。thinkingパラメータの世代不一致も同様です。Claude 4.7以降でthinking.type.enabled(extended thinking)を指定する場合、Claude 4.5以前でthinking.type.adaptiveを指定する場合、thinkingが常時有効なモデルにthinking.type.disabledを指定する場合、このいずれも400が返ります。prefill不可のエラー(後述)とあわせて、400の大半はモデル世代とリクエスト内容のミスマッチが原因です。

Anthropic APIの全体像(モデル選択・料金体系・実装の入口)はAnthropic API完全ガイドで扱っています。本記事はそこから独立させた、エラー分類とハンドリング設計に絞った専門編です。

リトライ設計 — 自動リトライとRetry-Afterの境界

SDKは一時的な失敗を既定で自動リトライします。対象は接続エラー・レート制限・5xx系サーバーエラーで、指数バックオフをかけながら既定2回まで再試行し、retry-afterヘッダーがあればその秒数を優先します。各SDKクライアントには最大リトライ回数を設定・無効化するオプションがあります。

529のoverloaded_errorは、全ユーザー横断でトラフィックが増えたときに起こります。まれなケースとして組織側の利用が急増すると429が出ることもあり、これはアクセラレーション制限と呼ばれる仕組みによるものです。トラフィックを段階的に増やし、利用パターンを一定に保つことがこの制限を避ける実務的な手立てです。

Claude Codeでは、このリトライ待機中にWaiting for API responseという表示が出ます。表示の意味は「waiting for api response」の意味と対処で扱いました。ただしClaude CodeやWeb版のチャットで日常的に当たる429の大半は、実装のリトライ設計ではなくサブスクリプションの使用量上限が原因です。この切り分けはClaude rate limit(レート制限)エラーの対処にまとめています。本記事が焦点を当てるのは、API実装側でこの429をどう受け止め設計するかです。

SDKの型付き例外でハンドリングする

公式SDKは、生JSONの代わりに型付き例外を投げます。クラス名と名前空間は言語ごとに違うため、文字列マッチではなく型で分岐するのが公式の推奨パターンです。

言語404の例外クラス判定方法
Python404の例外クラスanthropic.NotFoundError判定方法例外クラスでcatch
Ruby404の例外クラスAnthropic::Errors::NotFoundError判定方法例外クラスでcatch
Java404の例外クラスcom.anthropic.errors.NotFoundException判定方法例外クラスでcatch
Go404の例外クラス*anthropic.Error(単一の値)判定方法StatusCodeフィールドで分岐

Goだけは例外機構を使わず、単一の*anthropic.Error型の値を返し、StatusCodeフィールドで分岐する設計です。他の言語のtry/catchに慣れていると見落としがちな違いで、Goで実装する場合はこの一点だけ設計方針が変わります。

try:
    message = client.messages.create(
        model="claude-sonnet-5",
        max_tokens=1024,
        messages=[{"role": "user", "content": "Hello"}],
    )
except anthropic.NotFoundError:
    ...  # 404: リソースIDを確認
except anthropic.RateLimitError:
    ...  # 429: retry-afterを見て待つ
except anthropic.APIStatusError:
    ...  # その他の4xx/5xx
except anthropic.APIConnectionError:
    ...  # ネットワーク層の失敗

具体的なクラスから汎用的なクラスへ、この順で並べるのが公式の推奨です。逆順にするとAPIStatusErrorが先に全部を拾ってしまい、404固有の分岐に届きません。

ストリーミング中のエラーとその回復

ストリーミングのレスポンスはSSE(Server-Sent Events)で届き、最初にHTTP 200が返ったあとでもエラーが発生します。この場合、通常のステータスコードによる分岐は使えません。

event: error
data: {"type": "error", "error": {"type": "overloaded_error", "message": "Overloaded"}}

高負荷の時間帯には、このイベントとしてoverloaded_errorが届きます。非ストリーミングであれば529に対応する状況が、ストリーミングでは200のレスポンスの途中に挟み込まれる形です。イベントストリームを処理するコードには、errorイベント専用のハンドラが要ります。

接続断やタイムアウトで中断した場合、部分的に受け取った内容を保存し、続きを要求する形でリカバリーできます。ただし続きの要求の組み立て方は、モデル世代で変わります。Claude 4.5以前のモデルでは、受け取った部分応答をassistantメッセージの続きとして送り直します。Claude 4.6以降はこの方式が使えません。現行モデルはassistantメッセージのprefill(事前入力)自体を拒否するため、部分応答をuserメッセージに含めて「続きから再開してください」と指示する形に変わります。

この違いは、400のprefill不可エラーと表裏の関係です。prefillを拒否する設計に合わせて、リカバリー手順そのものが世代で変わっている形になります。ツール呼び出しや思考ブロックは部分的な再開ができないため、直近のtextブロックまで巻き戻して再開するのが公式の推奨です。

リクエストサイズの上限とタイムアウトの設計

リクエストサイズの上限はエンドポイントごとに違います。

エンドポイント上限
Messages API上限32MB
Token Counting API上限32MB
Batch API上限256MB
Files API上限500MB

超えると413のrequest_too_largeが返ります。直接APIを使っている場合、この413はリクエストがAPIサーバーに届く前にCloudflareが返しており、Anthropic側の処理にすら入っていません。

504のtimeout_errorは処理中のタイムアウトです。長時間かかる処理は、非ストリーミングで待つのではなくストリーミングAPIに切り替えるのが公式の対処です。SDKは非ストリーミングのMessages APIリクエストが10分のタイムアウト見込みを超えないか事前に検証しており、超えそうな設定は実行前に弾かれます。ネットワークによってはアイドル接続を一定時間で切断するため、max_tokensを大きく取ったまま長時間待つ設計は失敗しやすくなります。直接APIに接続する実装では、TCPのkeep-aliveを設定しておくとアイドルタイムアウトの影響を抑えられます。

Batch APIではエラーが例外でなくデータになる

Batch APIでは、エラーが例外として返ってきません。個々のリクエストの結果は、バッチ処理が終わったあとにデータとして受け取ります。

result type内容
succeeded内容成功。メッセージ結果を含む
errored内容エラー発生。不正なリクエストか内部エラー。課金なし
canceled内容バッチ完了前にキャンセル。課金なし
expired内容24時間の有効期限切れ。課金なし

errored・canceled・expiredはいずれも課金対象外です。失敗したリクエストのために料金を払う設計にはなっていません。個々の結果はcustom_idで元のリクエストと対応づけられます。まずresult.typeで4種類のどれかを判定します。erroredならさらにerror.error.typeinvalid_request_errorかそれ以外かで分岐する、2段階の判定です。

for result in results:
    match result.result.type:
        case "errored":
            if result.result.error.error.type == "invalid_request_error":
                ...  # リクエスト側の不備。修正して再送
            else:
                ...  # サーバー側エラー。同じ内容で再送可
        case "expired":
            ...  # 24時間以内に処理されなかった。再送

この設計は、Messages APIの例外ベースの分岐とは前提が違います。処理が自分のプロセスの外で進むため、例外という同期的な仕組みが成立しません。結果を取りに行く側が、届いたデータを見て分岐する形に変わります。

erroredの主因は、送信前の設計ミスにも潜んでいます。1バッチは10万リクエストまたは256MBのどちらか早いほうで頭打ちになり、超えるリクエストはそもそも受け付けられません。結果を取得できる期間にも注意が必要です。処理が終わらないまま24時間が経つと個々のリクエストがexpiredになりますが、これとは別に、無事完了した結果自体もバッチ作成から29日で取得できなくなります。24時間の期限(未処理分の失効)と29日の期限(完了済み結果の保存期限)は別物です。パラメータ側の制約もあります。stream: trueはバッチの結果が単一ファイルで返る設計と噛み合わないため使えません。max_tokens: 0によるキャッシュの事前温め(pre-warming)もバッチ内では未対応です。どちらも送信するとinvalid_request_errorが返ります。

Claude APIのエラーは「待てば治る」と「直さないと治らない」で分かれる

ここまでの仕様を並べると、1つの設計思想が見えてきます。Claude APIは、エラーの原因が呼び出し側にあるかAnthropic側の一時的な状態にあるかで、対応の主体を明確に分けています。400番台の大半はリクエストを直さない限り解決せず、SDKもここではリトライを一切行いません。429・500・504・529は逆に、SDKが既定で2回まで肩代わりします。

ストリーミングとBatch APIは、この線引きを別の形で徹底したものと見ることができます。ストリーミングは200のあとに来るエラーを専用のイベントに分離し、通常のステータスコード分岐を素通りさせません。Batch APIはさらに踏み込み、エラーを例外という同期的な仕組みから外し、非同期の結果データに変えています。どちらも、「リトライで直る層」と「直さないと直らない層」を、プロトコルの構造そのもので分けている点は共通です。

すべての失敗を1つのtry/exceptでまとめて再送する実装は、この設計の上では損をします。400と429を同じ扱いでリトライすると、直らないエラーへの無駄なリクエストが、直るはずのエラーの待ち時間を圧迫します。型で分岐する実装のほうが、同じ障害発生率でも復旧が速くなります。

よくある質問

429とoverloadedはどちらも自動リトライの対象ですか

どちらも対象です。SDKは接続エラー・レート制限・5xx系エラーを既定で2回まで指数バックオフ付きの自動リトライの対象にし、retry-afterヘッダーがあればその秒数を優先します。ただし、使用量上限に達した429は待っても同じ結果が返るため、リトライだけでは解決しません。

request_idはいつ確認すればよいですか

サポートへ問い合わせるときに使います。レスポンスボディのrequest_idとヘッダーのrequest-idは同じ値です。Claude Platform on AWSではAWSのリクエストID(x-amzn-requestid)も別に返るため、CloudTrail照会にはそちらを使います。

ストリーミングでエラーが起きたら通常のエラーハンドリングと同じ形で拾えますか

拾えません。ストリーミングはHTTP 200が返ったあとにイベントとしてエラーが届くため、ステータスコードによる分岐では検出できません。SSEのerrorイベントを専用に処理するハンドラが必要です。

Batch APIのエラーはどう検知しますか

例外は発生しません。バッチ処理が終わったあと、個々のリクエストの結果をresult.typeで判定します。errored・canceled・expiredはいずれも課金対象外です。

400エラーはリトライすれば直りますか

直りません。invalid_request_errorはリクエストの形式や内容の不備が原因のため、同じ内容で再送しても同じエラーが返ります。パラメータを修正してから再送する必要があります。

Claude CodeやDesktopでの利用時のエラーもこの記事の対象ですか

直接対象にしているのは、プログラムからAPIを呼び出す実装の設計です。Claude CodeやWeb版チャットでの日常利用中に429やoverloadedに当たったときの対処は、Claude rate limit(レート制限)エラーの対処にまとめています。

まとめ

Claude APIのエラーは、原因の所在で対処が変わります。呼び出し側の不備で起きる400番台の大半は、修正しない限り再送しても解決しません。529・500・504・使用量と無関係な429は、SDKの既定リトライが指数バックオフとretry-afterを使って肩代わりします。この境界を実装のどこで引くかが、エラーハンドリング設計の起点です。

ストリーミングでは200のあとに届くerrorイベントを別ハンドラで拾い、Batch APIでは例外の代わりにresult.typeで結果を判定します。プロトコルの形が変われば、同じ境界線でも実装の置き場所が変わります。型付き例外・retry-afterrequest_idのログ、この3点を最初から設計に組み込んでおくと、障害発生時の切り分けにかかる時間が大きく減ります。

この記事を共有:XはてブLinkedIn