Claude Media
プログラマティックツールコーリングは4分でタイムアウトし多くの場合リトライされる

プログラマティックツールコーリングは4分でタイムアウトし多くの場合リトライされる

プログラマティックツールコーリングの保留は約4分でTimeoutErrorになります。5分弱で起きるコンテナのアイドル失効と混同しやすい、公式ドキュメント記載の実際のエラー文言と対策を扱います。

このTipsでできること

プログラマティックツールコーリングでツール結果を返すのが遅れると、Claudeのコード側でTimeoutErrorが発生します。この待ち時間の上限は約4分ですが、コンテナ自体がアイドル状態で失効するまでの約5分とは別の数字です。この記事では、保留から実際にタイムアウトするまでの挙動、公式ドキュメントに載っている実際のエラー文言、そして2つの「時間切れ」を混同しないための見分け方をまとめます。

保留状態とは何か — コードがtool_resultを待って一時停止している

プログラマティックツールコーリングでは、Claudeがコード実行環境の中でツールを関数として呼び出します。ツール関数が呼ばれると、コード実行はそこで一時停止し、APIはstop_reason: "tool_use"とともにレスポンスを返します。このtool_useブロックのcallerフィールドには、呼び出し元となったcode_executionブロックのidが入ります。

{
  "type": "tool_use",
  "id": "toolu_def456",
  "name": "query_database",
  "input": { "sql": "<sql>" },
  "caller": {
    "type": "code_execution_20260120",
    "tool_id": "srvtoolu_abc123"
  }
}

ここで止まっているのは、新しいツール呼び出しの起動待ちではなく、すでに実行が始まっているPythonコードがtool_resultを待って一時停止している状態です。tool_resultを送るとコードの実行が再開され、awaitで待っていた関数呼び出しに戻り値が渡ります。この一時停止と再開のやり取りは複数回の往復を経ることもあります。

保留から約4分でTimeoutErrorになる

公式ドキュメントは、保留中のツール呼び出しについて次のように警告しています。ツール結果が届かないまま約4分が経過すると、Claudeの実行中のコード内でTimeoutErrorが発生します。

{
  "type": "code_execution_tool_result",
  "tool_use_id": "srvtoolu_abc123",
  "content": {
    "type": "code_execution_result",
    "stdout": "",
    "stderr": "TimeoutError: Calling tool ['query_database'] timed out (no response after 270s).",
    "return_code": 0,
    "content": []
  }
}

このエラーメッセージ自体が興味深い数字を含んでいます。本文の説明は「約4分」(240秒)ですが、実際に示されているエラー文言は270s、つまり4分30秒です。どちらも同じ公式ドキュメント内の記載で、矛盾というより「約4分」という説明のとおり厳密に240秒で切れるわけではないことを示す実例と見るのが妥当です。実装側でタイムアウト前提の設計をするときは、この270秒という実例値をそのまま基準にするのではなく、後述のとおり240秒より十分短い側に自前のタイムアウトを置いておくのが確実です。

重要なのは、TimeoutErrorreturn_code: 0とともに返ってくる点です。プロセスがクラッシュしたわけではなく、stderrに例外メッセージが書き込まれた状態でコードの実行自体は継続しています。エラーハンドリングを書くときは、return_codeだけでなくstderrの中身も見てTimeoutErrorかどうかを判定する必要があります。

タイムアウト後にClaudeは何をするか

公式ドキュメントは、TimeoutErrorが発生した後の挙動について次のように説明しています。

Claude sees the error in stderr and typically retries the call

Claudeはstderrに出たTimeoutErrorを読み取り、多くの場合そのツール呼び出しを再試行します。ただし"typically"(通常は)という留保がついている表現で、必ず再試行することが保証されているわけではありません。何度再試行してもツール結果が返ってこない状況を想定するなら、実装側のツール実行そのものにタイムアウトを設け、TimeoutErrorが繰り返し発生しないようにしておくのが安全です。

「4分のタイムアウト」と「5分弱のコンテナ失効」は別物

この4分という数字は、もう1つの時間指標と混同しやすい性質を持っています。プログラマティックツールコーリングが使うコンテナは、コード実行ツールの料金体系で扱ったとおり、操作が無い状態が続くと約5分でアイドル失効(チェックポイント化)します。この2つは発生条件も影響範囲も異なります。

ツール呼び出しの待機タイムアウトコンテナのアイドル失効
発生までの時間ツール呼び出しの待機タイムアウト約4分(実例では270秒)コンテナのアイドル失効約5分
何を待っているかツール呼び出しの待機タイムアウト保留中のtool_result 1件コンテナのアイドル失効コンテナ全体への操作
発生時の挙動ツール呼び出しの待機タイムアウトコード内でTimeoutError、コードは継続コンテナのアイドル失効コンテナがチェックポイント化される
影響範囲ツール呼び出しの待機タイムアウト該当するツール呼び出し1回分コンテナのアイドル失効コンテナの状態全体

公式ドキュメントは、この2つを混同しないよう「一時停止したレスポンスに含まれるexpires_atのタイムスタンプより十分早くツール結果を返すこと」と警告しています。ツール結果を4分以内に返せばTimeoutErrorは避けられますが、それとは別にコンテナ自体のアイドル失効(約5分)のカウントダウンも進んでおり、レスポンスを送り返すまでにさらに間が空くと、今度はコンテナ側の失効に引っかかる可能性が出てきます。expires_atはコンテナ側の残り時間を示す指標であり、4分の待機上限そのものは公式ドキュメントの記載どおり自前で把握しておく必要があります。

似ているが別物の第3の時間制限 — 90秒のセル制限とexecution_time_exceeded

「時間切れ」に関わる公式ドキュメントの記載はもう1つあります。コード実行ツールには、ツール呼び出し全体に共通のexecution_time_exceededという上限があり、1回のツール呼び出しがこの最大実行時間を超えると発生します。これはツールバージョンを問わず全ツール共通のエラーコードです。

これとは別に、プログラマティックツールコーリングの中で実行されるPythonの1セルごとに90秒のウォールクロック制限が存在します。この制限自体はcode_execution_20260120code_execution_20260521のどちらでも共通です。両バージョンの違いは、code_execution_20260521ではこの90秒制限をツールの説明文でClaudeに明示的に伝える点だけで、ランタイムとしての挙動は同一です。セルがこの制限を超えると、例外にはならず、return_codeが非ゼロの通常のコード実行結果として返り、出力の中にdetection_timeoutというステータスメッセージが含まれます。

整理すると、名前が似た3つの時間制限は発生層がすべて異なります。

制限発生する層発生時の形
保留中tool_resultの待機(約4分)発生する層プログラマティックツールコーリングの一時停止1回分発生時の形コード内でTimeoutErrorreturn_code: 0
Pythonセルのウォールクロック制限(90秒)発生する層プログラマティックツールコーリングのPythonセル1つ(バージョン共通)発生時の形return_codeが非ゼロ、detection_timeout
ツール呼び出し全体の最大実行時間発生する層ツール呼び出し1回分(全バージョン共通)発生時の形execution_time_exceededエラー

自分のツールが遅いのか、Claudeが書いたコードのセルが重いのか、ツール呼び出し自体が長すぎるのかを切り分けるには、まずどの形でエラーが返ってきたかを見るのが早道です。stderrTimeoutErrorの文字列があれば1つ目、detection_timeoutならセル制限、error_codeフィールドにexecution_time_exceededが入っていれば3つ目、という判定順序になります。

4分以内に返すレスポンスにも組み立て上の制約がある

保留中にtool_resultを送り返すリクエストには、タイムアウトの数字とは別に、見落とすと400エラーになる制約が2つあります。

1つ目は、containerのIDを必ず含めることです。保留中のプログラマティックツール呼び出しがある状態では、containerのIDは省略可能なオプションではなく必須項目になります。付け忘れると、APIはリクエストそのものを拒否します。通常のコード実行では省略しても新しいコンテナが作られるだけですが、保留中の呼び出しに対してはこの挙動が通用しません。

2つ目は、tool_resultを返すメッセージの中身です。保留中のプログラマティックツール呼び出しに応答するとき、そのユーザーメッセージにはtool_resultブロックだけを含める必要があります。テキストブロックを一緒に含めると、たとえtool_resultの後ろに置いたとしても拒否されます。Claudeに追加の指示や補足情報を伝えたい場合は、ターンが完了してから別のユーザーメッセージとして送る必要があり、保留中の継続リクエストに混ぜることはできません。この制約はtool_resultcontent自体にも及び、文字列またはtextブロックのみが許可されます。画像やドキュメントのようなブロック型は拒否されます。

どちらの制約も、タイムアウトそのものではなくエラーの種類としてはinvalid_request_error400で返ってきます。TimeoutErrorと紛らわしいのは、どちらも「保留中の応答をうまく返せていない」という同じ症状として現れる点です。実装側でエラーハンドリングを書くときは、stderrに含まれる文字列を見る経路(コード内のタイムアウト)と、HTTPステータス・エラーコードを見る経路(リクエスト形式の不備)を最初から分けておくと、原因の切り分けに迷わずに済みます。

実装側で対策する3点

公式ドキュメントがタイムアウト対策として挙げているのは次の3点です。

  • expires_atフィールドを監視する: 一時停止したレスポンスに含まれるこの値を見て、残り時間を把握する
  • ツール実行そのものにタイムアウトを実装する: 外部APIの呼び出しやDBクエリが長時間ハングしないよう、呼び出し側の実装にも上限を設ける
  • 長時間処理は小さなチャンクに分割する: 1回のツール呼び出しに時間のかかる処理を詰め込まず、複数回に分けてtool_resultを返しやすくする

これらはいずれも「ツール結果を4分以内に返す」ための対策であり、コード実行の一時停止そのものを長引かせない設計に寄せることが基本方針です。サーバーツールとクライアントツールが混在するときの継続リクエストでも触れているとおり、継続リクエストのtools配列を維持し、containerのIDを正しく渡すところまで含めて一連の実装として揃えておく必要があります。プログラマティックツールコーリングの全体像はAdvanced Tool Useにまとめてあります。

実装上は、遅くなりうるツール(外部APIのリトライ待ち・大きなクエリ・ネットワーク越しの処理)側に、4分より十分短い自前のタイムアウトを設定しておくのが確実です。ツール側で先に打ち切って明示的なエラー文字列をtool_resultとして返せば、Claudeのコードはその内容を見て代替の処理に分岐できます。何も返さずに4分間黙って待たせてしまうと、Claudeが受け取れる情報はTimeoutErrorという汎用的な文字列だけになり、失敗の理由(タイムアウトなのか、ネットワーク断なのか、認証切れなのか)を判別する材料がありません。

タイムアウトの設計思想をどう読むか

4分という待機上限と、ツール実行側での自前タイムアウトの推奨は、遅いツールをコード実行の外に置き去りにしないという設計だと読めます。プログラマティックツールコーリングの主な狙いは、多数のツール呼び出しをコードとしてまとめ、モデルの推論を挟まずに処理することです。もし1つのツール呼び出しが数分単位で固まっても構わない設計だったなら、コンテナ全体の応答性が崩れ、まとめて処理する利点が薄れます。約4分という上限は、個々のツール呼び出しに「無限に待たない」という前提を強制することで、バッチ処理としての性質を保つための仕組みと見るのが妥当です。

まとめ

プログラマティックツールコーリングでツール結果を待っている状態は、約4分(実例のエラー文言では270秒)でTimeoutErrorになります。これはコンテナ自体のアイドル失効(約5分)とは別の時間軸で進むカウントダウンです。Claudeはstderrのエラーを見て多くの場合リトライしますが、保証された挙動ではないため、expires_atの監視・ツール実行側のタイムアウト実装・長時間処理の分割という3点を実装側でも備えておくのが安全です。

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