CLAUDE_ASYNC_AGENT_STALL_TIMEOUT_MSとは — サブエージェントのストール検知の設定
CLAUDE_ASYNC_AGENT_STALL_TIMEOUT_MSは、進捗のないサブエージェントを強制終了する環境変数です。既定値の連動先、中断後の挙動、他の制御との使い分けをまとめます。
CLAUDE_ASYNC_AGENT_STALL_TIMEOUT_MS は、サブエージェントの「進捗なし」を判定するミリ秒数を変える環境変数です。既定は600000(10分)。ストリーミングイベントが一定時間届かないと、Claude Codeはそのサブエージェントを強制終了し、親セッションへストールを報告します。バックグラウンドサブエージェントではタスクが失敗として記録され、途中までの出力も一緒に残ります。ストールとは別に、非同期サブエージェントの完了通知がストリーミングモードで拒否される既知のバグもあり、このエラーの原因にまとめています。
この変数が見ているのは「時間」ではなく「イベントの間隔」
判定の単位は、経過時間の合計ではなくストリーミングイベントの間隔です。サブエージェントがトークンを吐き続けている限り、実行が何分続いてもタイマーはその都度リセットされます。逆に、1つのイベントも来ないままCLAUDE_ASYNC_AGENT_STALL_TIMEOUT_MSの秒数が経過すると、そこで強制終了されます。
似た名前の制御がいくつかあるため、何を測っているかで整理すると区別しやすくなります。
| 変数 / フィールド | 検知条件 | 対象範囲 |
|---|---|---|
CLAUDE_ASYNC_AGENT_STALL_TIMEOUT_MS | 検知条件ストリーミングイベントが一定時間来ない | 対象範囲サブエージェント単位 |
maxTurns(サブエージェント定義) | 検知条件ターン数が上限を超える | 対象範囲サブエージェント単位 |
API_TIMEOUT_MS | 検知条件1リクエストが時間内に終わらない | 対象範囲メインループ + 全サブエージェント |
CLAUDE_CODE_MAX_CONCURRENT_SUBAGENTS | 検知条件同時実行数が上限を超える | 対象範囲セッション単位 |
API_TIMEOUT_MSは1回のAPIリクエストの制限時間で、既定は同じ600000(10分)ですが、メインの会話にもすべてのサブエージェントにも一律にかかります。一方CLAUDE_ASYNC_AGENT_STALL_TIMEOUT_MSはサブエージェント専用で、リクエスト1本の長さではなく「動いている気配があるか」だけを見ます。長い1回のリクエストを許容しつつ、完全に応答が止まったケースだけを狙って検知したいときに使う変数です。
既定値はストリーミングウォッチドッグと連動する(v2.1.257から)
CLAUDE_ASYNC_AGENT_STALL_TIMEOUT_MSの既定値は固定ではありません。ストリーミングアイドルウォッチドッグ(CLAUDE_ENABLE_STREAM_WATCHDOG。全プロバイダーで既定オン)が有効な間は、既定値がCLAUDE_STREAM_IDLE_TIMEOUT_MSに5分を足した値になります。CLAUDE_STREAM_IDLE_TIMEOUT_MS自体の既定は300秒(5分)なので、通常は5分+5分=600000ミリ秒に落ち着きます。ウォッチドッグをオフにしている場合は、連動を外れて既定600000に固定されます。
v2.1.257より前は、この連動そのものが無く、既定値は常に600000固定でした。つまりCLAUDE_STREAM_IDLE_TIMEOUT_MSをどれだけ伸ばしても、サブエージェントのストール検知は10分のままだった状態から、v2.1.257で「ストリーミング応答が止まってから何分待つか」を1箇所の変数でまとめて伸ばせる作りに変わっています。
強制終了後に何が起きるか — フォアグラウンドとバックグラウンドで扱いが違う
タイマーが切れたあとの挙動は、そのサブエージェントがフォアグラウンドとバックグラウンドのどちらで動いていたかで変わります。
- フォアグラウンド: サブエージェントを中断し、ストールが起きたことを親セッションへ報告します
- バックグラウンド: 中断に加えてタスクが失敗として記録され、通知には途中までの結果(partial result)が添えられます。途中まで進んだ作業を失わないための仕組みです
ストールで失敗したサブエージェントは、プロンプト下のパネルに30秒間残ります。xキーを押せばその場で行を消せます。ただし、これは/tasksの一覧そのものには乗りません。/tasksが「done」として拾うのは成功終了したサブエージェントだけで、失敗・停止したものはパネルからだけ確認できる非対称な扱いです。詳細はClaude Codeの/tasksコマンドでサブエージェントの完了を確認するにまとめています。
フォアグラウンドとバックグラウンドはどちらで動くか
サブエージェントがどちらで動くかは、Claudeやユーザーが毎回指定するわけではなく、いくつかの条件で自動的に決まります。
- 対話セッションでは既定でfork modeがオンで、この場合フォークかどうかを問わずバックグラウンドで動きます。Claudeが結果を先に見たくてもフォアグラウンドへは切り替えられません
- 非対話モード(
-p実行)とAgent SDKでは、fork modeが既定オフです。この場合Claudeは基本バックグラウンドで動かし、結果をすぐ使いたいときだけフォアグラウンドを選びます CLAUDE_CODE_DISABLE_BACKGROUND_TASKS=1を設定すると、fork modeの設定によらず常にフォアグラウンドで動きます- サブエージェント定義のfrontmatterで
background: trueを指定すれば、Claudeが結果を先に必要としていてもそのサブエージェントだけバックグラウンドに固定できます - agent teamsのteammateがAgentツールでサブエージェントを起動した場合は、上記のどの条件よりも優先して常にフォアグラウンドで動きます。そのサブエージェント定義に
background: trueが指定されていると、Claude Codeはエラーで起動を拒否します
つまりClaude Codeを対話的に使っている限り、日常的に起こるストールの大半はバックグラウンドの経路をたどります。
ストールで失敗したサブエージェントを再開する
失敗として記録されたサブエージェントも、完了済みの他のサブエージェントと同じ手順で再開できます。ClaudeがSendMessageツールに同じエージェントIDやnameをtoとして指定すると、新しいAgent呼び出しを起こさずバックグラウンドで再開します。再開した実行は元の実行が持っていたツールセットをそのまま引き継ぎ、元の実行がウォームアップしたプロンプトキャッシュも引き続き読めるため、ゼロから始め直すより効率的です。
再開したサブエージェントは同じIDのまま新しい実行として動き出すため、タスク一覧では一度「失敗」と表示されていたものが再び「実行中」に変わります。
ただし/tasksのxキーで自分から止めたサブエージェントは自動では再開されません。パネルにその行が残っている間にトランスクリプトへ直接入力すれば自分で再開でき、それ以降はClaudeからのメッセージでも再開できるようになります。一方TaskStopツールで止めた場合は、停止済みの実行が終了した後にClaudeがSendMessageで呼びかければ自動的に再開します。ストールによる自動中断と、意図した手動停止とでは、後始末の扱いが異なる点に注意してください。
再開が失敗するパターンもあります。v2.1.199以降、SendMessageは指定された名前が会話中で以前に到達したのと同じエージェントを指しているかを確認します。ストールしたサブエージェントと同じ名前を持つ別のバックグラウンドサブエージェントが新たにspawnされ、名前が使い回されていた場合、Claude Codeは誤った相手に送らないよう送信を拒否し、その名前が現在どのエージェントを指すかをエラーで返します。この場合、ストールした側を再開するにはspawn時に受け取ったエージェントIDで名指しする必要があります。
サブエージェント数・深さの制限と組み合わせる
CLAUDE_ASYNC_AGENT_STALL_TIMEOUT_MSは「1つのサブエージェントが止まっていないか」を見る変数で、「いくつまで同時に動かせるか」を見る変数とは役割が分かれています。並列実行を前提としたワークフローでは、両方を目的に応じて調整することになります。
| 目的 | 触る変数 |
|---|---|
| 個々のサブエージェントの応答停止を早く検知したい | 触る変数CLAUDE_ASYNC_AGENT_STALL_TIMEOUT_MSを短くする |
| 同時に動かせるサブエージェント数を増減したい | 触る変数CLAUDE_CODE_MAX_CONCURRENT_SUBAGENTS |
| 個々のサブエージェントに割り当てるモデルを固定したい | 触る変数CLAUDE_CODE_SUBAGENT_MODEL |
同時実行数の既定値や、子サブエージェントを持てる階層の上限はCLAUDE_CODE_MAX_CONCURRENT_SUBAGENTSとは、モデル固定の仕組みはCLAUDE_CODE_SUBAGENT_MODELとはで扱っています。大量のサブエージェントを並列で走らせるワークフローほど、そのうちの1つが止まったまま同時実行枠を占有し続けるリスクも上がるため、CLAUDE_ASYNC_AGENT_STALL_TIMEOUT_MSを短めに設定して早期に手放す運用と相性がよくなります。
なぜサブエージェントだけに自動アボートの仕組みが要るのか
メインの会話でツール呼び出しが長引いているときは、30秒おきにtool_progressという経過報告(ハートビート)が流れ、長時間実行中なのか止まっているのかを区別できます。この経過報告は、サブエージェント内部のツール呼び出しには流れません。親セッションから見ると、サブエージェントが本当に作業を続けているのか、内部で止まっているのかを判別する手がかりが最初から存在しないことになります。CLAUDE_ASYNC_AGENT_STALL_TIMEOUT_MSが自動でアボートまで行うのは、この可視性の欠如を埋める役割を担っているためと言えそうです。tool_progressのハートビート自体もAgent SDK v0.3.214以降の機能で、v2.1.257より前はフォアグラウンドのAgentツール呼び出しにすら流れていませんでした。サブエージェントの進捗が外から見えにくいという課題は、比較的最近まで残っていたことになります。
設定のしかた
シェルの環境変数として渡します。
export CLAUDE_ASYNC_AGENT_STALL_TIMEOUT_MS=120000
claudeプロジェクトで固定値を共有したい場合はsettings.jsonのenvキーに書きます。
{
"env": {
"CLAUDE_ASYNC_AGENT_STALL_TIMEOUT_MS": "120000"
}
}Agent SDKからCLIサブプロセスを起動する場合は、queryのenvオプションに渡します。process.envを丸ごと置き換える仕様なので、PATHなどを引き継ぐには展開した上で追加します。
import { query } from "@anthropic-ai/claude-agent-sdk";
const result = query({
prompt: "Analyze this code",
options: {
env: {
...process.env,
CLAUDE_ASYNC_AGENT_STALL_TIMEOUT_MS: "120000",
},
},
});よくあるつまずき
CLAUDE_STREAM_IDLE_TIMEOUT_MSだけ伸ばして満足してしまう。v2.1.257以降は既定値が連動するため、意図せずCLAUDE_ASYNC_AGENT_STALL_TIMEOUT_MS側の検知時間まで一緒に伸びている- 無限ループのような「終わらないサブエージェント」を止める変数だと誤解する。トークンを出力し続けているならタイマーはリセットされ続けるため、ストール検知では止まらない。ターン数で打ち切りたいなら
maxTurnsが対象 - メインセッションが固まったときにもこの変数が効くと思い込む。対象はあくまでサブエージェントで、メインループの制御は
API_TIMEOUT_MSが担う - アボートされたバックグラウンドサブエージェントが自動で再開すると思い込む。再開にはClaude自身が
SendMessageで明示的に呼びかける操作が要る
まとめ
CLAUDE_ASYNC_AGENT_STALL_TIMEOUT_MSは、サブエージェントのストリーミングイベントが一定時間途切れたときに強制終了する仕組みのタイムアウトです。既定600000はv2.1.257以降CLAUDE_STREAM_IDLE_TIMEOUT_MSと連動するため、片方だけ調整すると意図しない副作用が出ます。フォアグラウンドは中断と報告だけですが、対話セッションで既定になるバックグラウンドサブエージェントは失敗として記録され、途中の出力ごと親に渡されます。並列実行数や深さの制限とは役割が別なので、大量のサブエージェントを回すワークフローでは組み合わせて調整してください。
関連する記事
Claude Code をもっと見る →Claude Code(クロードコード)とは — できること・料金・使い方・CLIから8つの拡張機構まで
only prompt commands are supported in streaming modeエラーの原因
「SDK execution error」でclaude-code-actionが落ちる原因と対処法
rate_limit_eventでMessageParseErrorが発生し停止する原因と対処
Agent SDKがDockerのDEBUG環境変数で動かなくなる原因と回避策
CLAUDE_CODE_MAX_WEB_SEARCHES_PER_SESSIONとは — 検索回数の上限を変える環境変数