retry exhaustion(リトライ枯渇)をClaude Codeで検知する方法
Claude CodeのAPIリトライが尽きた瞬間をOTelイベントで検知する手順。api_errorのattempt属性と実効上限の突き合わせ、名前が紛らわしいapi_retries_exhaustedの実際の発火条件まで確認します。
Claude Codeのリトライ枯渇はどう検知するか
Claude Codeは失敗したAPIリクエストを内部で自動的にリトライします。リトライの過程はログイベントとしては個別に残しません(トレースを収集していれば試行ごとのgen_ai.request.attempt span eventで追えます)。リトライを打ち切って失敗が確定したときにだけclaude_code.api_errorイベントが1回発行され、そのattempt属性が「合計で何回試行したか」を記録します。
リトライが尽きて完全に失敗した状態(retry exhaustion)を機械的に検知するには、このattemptの値を組織の設定(CLAUDE_CODE_MAX_RETRIESとCLAUDE_CODE_RETRY_WATCHDOG)から導いた実効上限と突き合わせる必要があります。単純に「attemptが大きい行を拾う」だけでは不十分です。設定によって上限が10回のときも300回のときもあるため、閾値をどこに置くかで誤検知と見逃しの両方が起こります。
claude_code.api_retries_exhaustedという名前のイベントも存在しますが、後述のとおりこの名前は文字どおりの「上限到達」を意味しません。名前だけで判断すると誤ったアラート設計になります。
組織全体でClaude Codeを使っているとき、リトライ枯渇に気づかないまま放置すると、個々の開発者が「セッションが止まった」「応答が返ってこない」と気づいてから初めて手動で再実行する運用になりがちです。プロバイダー側の障害やレート制限が一時的に集中したタイミングでは、この気づきの遅れがそのまま作業停止時間として積み上がります。OTelイベント側で検知しておけば、個々の開発者からの報告を待たずに障害の広がりを把握できます。
前提条件
| 項目 | 必要な状態 |
|---|---|
| テレメトリの有効化 | 必要な状態CLAUDE_CODE_ENABLE_TELEMETRY=1とイベント用のOTEL_LOGS_EXPORTER設定が済んでいること(設定手順はClaude CodeのOpenTelemetryで利用量とコストを可視化) |
| バックエンド | 必要な状態OTLPログを受信できる収集基盤 |
| Claude Codeのバージョン | 必要な状態CLAUDE_CODE_RETRY_WATCHDOG変数自体はv2.1.186以降で利用可能(既定300回化とキャップ解除はv2.1.199以降)、client_request_id属性はv2.1.214以降が必要 |
OTEL_METRICS_EXPORTERだけを設定してログ用のOTEL_LOGS_EXPORTERをnoneのままにしていると、api_errorもapi_retries_exhaustedもイベント側の信号なのでバックエンドに届きません。メトリクスとイベントは別のエクスポーター設定です。
リトライの実効上限はどう決まるか
CLAUDE_CODE_MAX_RETRIESはリトライ回数を上書きする環境変数で、既定値は10回です。v2.1.186以降、明示的に設定しても15回が上限としてキャップされます。
v2.1.199以降はCLAUDE_CODE_RETRY_WATCHDOGを1に設定すると、この上限そのものが変わります。評価ハーネスやCIジョブ、リモートワーカーのような無人セッション向けの設定で、一般的なトランジェントエラー(サーバーエラー・タイムアウト・接続切断)の既定リトライ回数を300回、バックオフ時間にしておよそ3時間まで引き上げ、CLAUDE_CODE_MAX_RETRIESを明示設定していれば15回のキャップも外します。さらに429と529の容量エラーは無期限にリトライします。バックオフは最大5分間隔で、レート制限のリセット時刻がレスポンスに含まれていればその時刻まで待ちます。
ただし例外が1つあります。支出上限や利用クレジット枯渇を示す429(ゲートウェイの支出上限のように一定周期でリセットされるものを含む)は、watchdogが有効でも即座に失敗します。v2.1.239より前のwatchdogはこれも無期限にリトライしていたため、古いバージョンでは挙動が異なります。
この関係は次のとおりです。
| 設定 | 一般的なトランジェントエラーの実効上限 | 枯渇時のattemptの目安 |
|---|---|---|
既定のまま(CLAUDE_CODE_MAX_RETRIES未設定) | 一般的なトランジェントエラーの実効上限10回 | 枯渇時のattemptの目安11 |
CLAUDE_CODE_MAX_RETRIESを上限の15回に設定 | 一般的なトランジェントエラーの実効上限15回 | 枯渇時のattemptの目安16 |
CLAUDE_CODE_RETRY_WATCHDOG=1(既定300回化はv2.1.199以降) | 一般的なトランジェントエラーの実効上限既定300回(429/529は無期限) | 枯渇時のattemptの目安301、またはそれ以上 |
上記より明らかに小さいattempt | 一般的なトランジェントエラーの実効上限— | 枯渇時のattemptの目安400のような非リトライ対象エラー、または支出上限429の可能性 |
attemptが小さいからといって「軽微な失敗」とは限りません。支出上限に達した429は1回で終わるため、attempt=1の行には設定ミスと予算超過という性質の異なる原因が混在します。ダッシュボードではstatus_codeとerrorも一緒に見て切り分けます。
api_errorのattempt属性で枯渇を判定する
アラートクエリの基本形は、claude_code.api_errorイベントをattemptの閾値でフィルタし、session.id・user.id・modelなどの標準属性で集計するというものです。閾値は組織でCLAUDE_CODE_RETRY_WATCHDOGを使っているかどうかで変わるため、固定値の11をハードコードすると、watchdog有効な環境では実際にはまだリトライ中の行まで「枯渇」と誤判定します。
閾値を組織の設定に合わせて可変にできない場合は、CLAUDE_CODE_RETRY_WATCHDOGを使うグループとそうでないグループでダッシュボードを分けるのが確実です。バックエンド側のクエリは概ね次の形になります。
※疑似コード
filter event.name == "api_error"
AND attempt >= threshold(user.group)
group by session.id, model, query_sourcequery_sourceで集計に加えると、メインセッションのrepl_main_threadから出たリトライ枯渇と、サブエージェントやコンパクト処理から出たものを分けて見られます。サブエージェントを大量に並列実行するワークフローでは、メインセッションは正常でもサブエージェント側だけでリトライが枯渇するケースがあるため、この区別がないと問題の所在を見誤ります。
コスト急増・トークン消費の異常・特定ユーザーからのセッション急増は、Claude Codeのテレメトリでよく設計される他のアラートです。リトライ枯渇のアラートはこれらと独立したルールですが、同じ時間帯に複数の信号が同時に動いた場合は、プロバイダー側の広域障害を疑う根拠になります。1つのバックエンドに集約しておけば、この時系列の重なりを別ダッシュボードを行き来せずに確認できます。
api_retries_exhaustedは名前ほど厳密ではない
claude_code.api_retries_exhaustedイベントは、api_errorと同時に発行される専用イベントで、total_attempts(合計試行回数)とtotal_retry_duration_ms(全試行を通した経過時間)を直接持っています。閾値の計算をせずに使える点はapi_errorより扱いやすい信号です。
ただし公式ドキュメントの定義は「1回を超える試行のあとにAPIリクエストが失敗したときに1回だけ記録される」というもので、これは必ずしも設定上の上限まで使い切ったことを意味しません。2回目の試行で400のような非リトライ対象エラーに転じて打ち切られた場合も、試行回数が1を超えていればこのイベントは発行されます。つまり「リトライが尽きた」というイベント名から連想する状態と、実際の発火条件にはズレがあります。
このズレを踏まえると、2つの信号は役割を分けて使うのが実用的です。
| 目的 | 使う信号 |
|---|---|
| リトライを伴った失敗がどれだけ起きているかの量的な把握、レイテンシへの影響測定 | 使う信号api_retries_exhaustedのtotal_attempts/total_retry_duration_ms |
| 設定上の上限まで使い切った「真の枯渇」だけをアラート対象にする | 使う信号api_errorのattemptを実効上限と突き合わせる |
total_retry_duration_msはapi_error側には無い属性なので、リトライによる遅延がユーザー体験にどれだけ効いているかを測るときはこちらが使えます。
セッションが回復したかを見分ける
api_errorが出たセッションが、その後も使い続けられたのか、それとも開発者がそこで作業を諦めたのかを区別するには、session.idでイベントをグループ化し、エラーより後のタイムスタンプでapi_requestイベントが存在するかを確認します。後続のapi_requestがあれば、セッションはエラーを乗り越えて処理を継続できています。無ければ、そのセッションはエラーの時点で止まった可能性が高いというシグナルになります。
Bedrock・Google Cloud's Agent Platform・Microsoft Foundry経由や直接APIキーで認証しているセッションにはuser.emailのようなClaudeアカウントの識別子が乗らず、user.idとsession.idだけが残ります。個々の利用者に紐づけて枯渇を追いたい場合は、OTEL_RESOURCE_ATTRIBUTESでenduser.idをmanaged settingsか起動ラッパー経由で埋め込む必要があります。Claude apps gatewayでサインインしたセッションだけは例外で、IdPの識別子がCLIによって自動的に付与されます。組織の設定配布そのものはmanaged settingsファイルで統制するため、OTEL_RESOURCE_ATTRIBUTESや閾値判定の元になる環境変数もそこに集約しておくと開発者ごとの設定漏れを防げます。
よくある質問
gateway経由のセッションでも同じ検知方法が使えますか
使えますが送信経路が変わります。/loginでClaude apps gatewayにサインインしたセッションは、ローカルでOTEL_EXPORTER_OTLP_ENDPOINTを設定していてもCLIのOTLP/HTTPエクスポート先はゲートウェイ固定になり、ゲートウェイが設定済みの転送先へ中継します。転送先を1つも設定していない信号は、ゲートウェイが受け取った上で破棄します。すでに自前のコレクターへ直接送っている場合は、そのコレクターをゲートウェイ側の転送先にも登録しておく必要があります。
client_request_idはattempt以外に何の役に立ちますか
client_request_idはクライアント側で生成されx-client-request-idヘッダーとして送られるUUIDで、サーバー側のrequest_idが取れないタイムアウトや接続エラーでも残ります。リクエストとレスポンスを突き合わせるキーとして、attemptが示す試行回数と組み合わせれば、どの試行がどのエラーで終わったのかを1件ずつ追跡できます。ただしファーストパーティAPI接続でのみ付与され、サードパーティプロバイダー経由や非ストリーミングのフォールバック経由では付きません。
メトリクスだけを送っていてもリトライ枯渇はわかりますか
わかりません。api_errorとapi_retries_exhaustedはどちらもイベント(ログ)として発行されるため、OTEL_LOGS_EXPORTERを設定していないとメトリクス側のコスト・トークン集計には一切現れません。利用量とコストの可視化を先に済ませている組織でも、この検知だけは別途ログ側のエクスポーターを有効にする必要があります。
よくあるつまずき
- ログ用エクスポーターの設定漏れ:
OTEL_METRICS_EXPORTERは設定したがOTEL_LOGS_EXPORTERを忘れていると、コストやトークンのメトリクスは届いてもリトライ枯渇のイベントは一切届きません - 閾値のハードコード:
attempt >= 11を固定で書くと、CLAUDE_CODE_RETRY_WATCHDOGを有効化したチームだけ誤検知が増えます。設定変更時にアラートルールの見直しを忘れがちです - イベント名からの思い込み:
api_retries_exhaustedという名前だけで「上限まで使い切った」と判断すると、途中で非リトライ対象エラーに切り替わった行まで枯渇として集計してしまいます - 支出上限429の見落とし: watchdogを有効にしていても、支出上限や利用クレジット枯渇による429は即座に失敗として記録されます。これをリトライ機構の不具合と誤診断しないよう、
errorとstatus_codeの内容まで確認します。支出上限そのものの設計はコスト管理の運用ルールに従います
まとめ
リトライ枯渇の検知は、claude_code.api_errorのattempt属性を組織の実効上限(既定11、CLAUDE_CODE_MAX_RETRIES設定時は16、CLAUDE_CODE_RETRY_WATCHDOG有効時は300超)と突き合わせて初めて正確になります。claude_code.api_retries_exhaustedは名前に反して「1回を超える試行での失敗」全般を指すため、量的な把握やレイテンシ測定に使い、真の枯渇判定はapi_error側で行うという役割分担が実用的です。組織でCLAUDE_CODE_RETRY_WATCHDOGを使っているグループを把握し、アラートの閾値をその設定に合わせて分けることが、誤検知を避ける最初の一歩になります。