Claude Codeのサブエージェントが応答しない・固まる問題を、GitHub Issueの実例で切り分けた
Claude Codeのサブエージェント(Task tool)が応答しない・Runningのまま固まる症状を、GitHub Issueの実例から3パターンに分類。公式にタイムアウト機能がない実情と、/tasks・環境変数を使った現実的な対処法を整理した。
エンジニアのゆとです。
案件でサブエージェントに調査を丸投げして、他の作業をしてから戻ってきたら画面が「Thinking…」のまま止まってた、という経験がある人は多いと思う。エスケープを押しても反応がない。/tasksを開いても該当タスクはin_progressのまま。かといって本当にハングしてるのか、単に重い処理を続けてるだけなのか、パッと見じゃ分からない。
自分も一度、レビュー用のサブエージェントを並列で3体走らせてたら、1体だけ結果が返ってこないまま20分近く経過したことがあった。他の2体はとっくに終わってる。ターミナルを再起動する以外に打つ手が見当たらず、結局そのセッションは強制終了する羽目になった。
調べてみると、これは自分の環境だけの問題じゃなかった。GitHub Issueには似た報告が大量にあって、しかもかなり長い間クローズされずに残っている。公式ドキュメントにも「サブエージェントがハングしたときのタイムアウト設定」についての記述は存在しない。この記事では、報告されている実例を整理して症状別に分類し、現状で取れる現実的な対処法をまとめた。
まず結論 — 「応答しない」は3パターンに分かれる
一口に「サブエージェントが固まる」と言っても、GitHub Issueを読み込んでいくと起きている場所が違う。この切り分けを先にやらないと、無駄な対処をすることになる。
| パターン | 症状 | 実際に起きていること |
|---|---|---|
| ① 本当にハングしている | 親セッションごと無反応、Escも効かない | サブエージェントのツール呼び出し(Bash/Glob等)の結果が返ってこず、親子間のIPCチャネルがデッドロックしている |
| ② 表示だけ固まっている | /tasksでin_progressのまま動かないが、実は作業自体は終わっている | サブエージェントの作業は完了・ファイルも書き込み済みなのに、完了状態が親セッションの内部状態に反映されていない |
| ③ バックグラウンドタスクがゾンビ化する | ネストしたバックグラウンドサブエージェントが、親が終わった後も「Running」表示のまま残る | 完了通知(completion notification)がキューに入ったあと配送されず、握りつぶされている |
厄介なのは、①と②はユーザー側からはほぼ見分けがつかないという点だ。両方とも「反応がない」ようにしか見えない。ただし対処法が違うので、後述する~/.claude/tasks/配下のファイルを確認する手順で切り分けるのが早い。
パターン①: Task()ツール自体にタイムアウトがない
これが一番報告数が多く、根本原因もはっきりしている。Claude CodeのTask()ツール(サブエージェント呼び出し)には、公式にタイムアウト機能が存在しない。サブエージェントが投げたツール呼び出し(Bash、Glob、WebFetchなど)の結果が何らかの理由で親に返ってこなくなると、親セッションはそこで無限に待ち続ける。
このIssueの報告者は2体のサブエージェントを並列実行していて、片方のcurl呼び出しが返ってこないまま止まった。もう片方は33メッセージ分の作業を完了していたのに、その結果すら親セッションに届かなかったという。ユーザーからの追加メッセージもキューに積まれるだけで一切処理されず、最終的にプロセスを手動でkillするしかなかった。
同じ症状はGlobツール呼び出しでも報告されている。30回のツール呼び出しのうち29回は正常に結果が返ってきて、1回だけ応答が消えたことで、サブエージェント全体が永久に待ち状態になったという事例だ。原因はOS・呼び出したツールの種類を問わず再現しており、報告はWindows・Linux双方から上がっている。
このIssueが興味深いのは、MCPサーバーを一切使っていない環境でも再現している点だ。つまり「MCPサーバーが原因」という説明では説明がつかない、もっと根本的な親子プロセス間の通信の脆さがあることになる。調査した結果、サブエージェントが書き出したファイルは全部きれいに完成していた。仕事自体は終わっていたのに、それを親に伝える経路だけが壊れていた、という点は覚えておく価値がある。
このIssueのコメント欄で興味深い指摘があった。フロントマターにmaxTurnsというフィールドがあり、これはサブエージェントが停止するまでの最大ターン数を設定できる。ただし3つの制約がある。
- カスタムサブエージェントの定義単位で設定するもので、呼び出しごとに変えられない
- 組み込みの
Explore・Plan・general-purposeサブエージェントには適用できない(実際に問題を起こしやすいのはこの組み込み系) settings.json側にも既定値を設定するキーが用意されていない
つまりmaxTurnsは「時間」ではなく「ターン数」の上限であり、今回のようなIPCレベルでの応答消失には効かない。今のところ、公式が用意している唯一の歯止めがこれだけという状況だ。
パターン②: 完了してるのにin_progressのまま動かない
パターン①より厄介なのがこっちだ。サブエージェントの作業自体は正常に終わっているのに、その完了がタスク状態に反映されず、UIも会話もin_progressのまま止まって見える。
このIssueには関連報告が束ねられていて、「完了したバックグラウンドBashタスクがサイドバーでRunningのまま残る」「セッションをまたいでバックグラウンドタスクが詰まる」「ゾンビ化したrunning表示のサブエージェントがstop-hookのループを引き起こす」など、症状の見た目は違うが根っこは同じというパターンが複数まとめられている。共通しているのは、プロセス自体の終了とタスク状態の更新がアトミックに行われていないという点だ。
コメント欄では、外部オーケストレーターからclaude -pを一定間隔で叩く運用をしている人からの報告もあった。サブプロセスは正常終了コード0で終わっているのに、直後に同じセッション/作業ディレクトリで別の呼び出しをすると、前のin_progress状態を引き継いでしまうケースがあるという。ヘッドレス運用や、CI・自動化ワークフローでサブエージェントを使っている人ほどこのパターンを踏みやすい。
パターン③: ネストしたバックグラウンドサブエージェントがゾンビ化する
サブエージェントをバックグラウンドで動かし、さらにそこから別のサブエージェントをネストで呼んでいる場合に特有のパターン。
これは完了通知(completion notification)がキューに積まれたあと、何らかの理由で配送されずに消えてしまうことで起きる。親から見ると「サブエージェントを再度呼び出したはずなのに一向に反応が返ってこない」状態になり、実質パターン①と同じ「待ちぼうけ」に見える。ネスト構成を使っている場合、まずこのパターンを疑うといい。

公式ドキュメントには何が書いてあるか
サブエージェントの公式リファレンスを確認したが、「ハングしたときのタイムアウト」を直接扱った記述は現時点で存在しない。代わりに、関連する挙動としてこの3つが明記されている。
手動での停止方法
/tasksコマンドを開き、停止したいサブエージェントの行を選んでxキーを押すと、そのサブエージェントを強制停止できる。プログラム的に停止させたい場合はTaskStopツールが用意されている。
/tasks
# 一覧から対象のサブエージェントを選択 → x キーで停止
ここで注意が必要なのは、v2.1.191以降、ユーザーが手動で停止したサブエージェントはSendMessageで自動的に再開されなくなったことだ。誤って止めてしまった場合、ユーザーがトランスクリプト上で明示的に再開しない限り動き出さない。
APIエラーで切断された場合の挙動
v2.1.199以降、レート制限やサーバーエラーでサブエージェントとの接続が切れた場合の挙動が定義されている。フォアグラウンドなら部分的な出力がある場合はそれを「切り詰められた」という注記付きで返し、出力が全くなければ「Agent terminated early due to an API error」というエラーを返す。バックグラウンドの場合はサブエージェントがfailed状態になり、最後の出力も含めて親に渡される。
これはこの記事で扱っているハング(応答が永久に返ってこない)とは別物で、どちらかというと接続断エラーの一種になる。エラーメッセージの見た目は似ているので混同しやすいが、対処法が違う。

完了済みタスクの永続化ファイルを見る
これが実務上いちばん役に立つ。サブエージェントのトランスクリプトは~/.claude/projects/{project}/{sessionId}/subagents/agent-{agentId}.jsonlに保存される。タスクの状態自体は~/.claude/tasks/{uuid}/配下にJSONで書き出されているので、「本当にまだ動いてるのか」「実はもう終わってるのか」をこのファイルの中身とタイムスタンプで確認できる。
# タスク状態ディレクトリを確認(セッションIDは/tasksの表示から控える)
ls -la ~/.claude/tasks/
# 各タスクのstatusとmtimeを見る
cat ~/.claude/tasks/<uuid>/*.json | grep -E '"status"|"name"'
前述の#49150のケースでは、この方法で「サブエージェントは全ファイルを書き終えていた」ことが事後的に判明している。ファイルの更新が止まったタイムスタンプ以降ずっと沈黙が続いているなら、パターン①(本当にハング)の可能性が高い。逆に成果物が出揃っていてstatusだけが古いままなら、パターン②(表示バグ)の可能性が高く、無理にプロセスを殺さずセッションを再起動するだけで復旧することがある。
実務でできる対処・予防策
タイムアウト機能がない以上、根本解決は待つしかないが、被害を小さくする手はいくつかある。
1. 並列度とネスト深さを絞る
サブエージェントを並列で大量に走らせるほど、どれか1つがハングしたときに気づきにくくなる。同時実行数とネストの深さは環境変数で調整できる。
export CLAUDE_CODE_MAX_CONCURRENT_SUBAGENTS=5
export CLAUDE_CODE_MAX_SUBAGENT_SPAWN_DEPTH=1
ネストを使わない設計にするだけでも、パターン③のゾンビ化は起きなくなる。並列実行数の変更履歴と暴走コストの背景はこちらにまとめている。

2. 単発の軽いタスクをわざわざサブエージェントに投げない
#61405の12時間ハングは、本来10秒で終わるはずのWeb検索1回をサブエージェントに投げたことがきっかけだった。単発で軽いタスクなら、素のWebFetchやWebSearchをメインの会話で直接叩いたほうが、ハングした場合の被害が小さい。サブエージェントに投げるべきかどうかの判断基準は以前の記事で整理した。

3. カスタムサブエージェントにはmaxTurnsで歯止めをかける
組み込みエージェント(Explore/Plan/general-purpose)には効かないが、自作のカスタムサブエージェントを使っているなら、フロントマターにmaxTurnsを設定しておくと際限のない暴走だけは防げる。
---
name: research-agent
maxTurns: 15
---
タイムアウトの代わりにはならないが、「終わらないタスクをずっと投げ続ける」暴走パターンには一定の効果がある。
4. 応答がないと感じたら、まず/tasksと状態ファイルで切り分ける
いきなりターミナルごと落とす前に、/tasksでどのタスクが止まっているかを確認し、前述の~/.claude/tasks/配下のファイルで実際の進捗を見る。成果物が出来上がっているなら、セッションを再接続するだけで復旧する可能性がある(パターン②)。ファイルの更新も止まっているなら、xキーでの強制停止か、それでも反応がなければプロセスの再起動に進むしかない(パターン①)。
FAQ
タイムアウトを自分で設定する方法はない?
現時点(2026年8月)では公式に用意されていない。GitHub Issue上ではhooksを使ってサブエージェントの生存監視をする非公式のワークアラウンド(dispatch liveness watchdog)がコミュニティから提案されているが、これはあくまで「ハングに気づきやすくする」ものであり、ハング自体を解消する仕組みではない。根本修正はAnthropic側のTask-toolレイヤーでの対応待ちになっている。
Connection closed mid-responseと何が違う?
似たエラーに見えるが別物。今回扱っているのはツール呼び出しの結果が永久に返ってこない「応答の消失」で、エラーメッセージすら出ない。Connection closed mid-responseは逆に明示的なエラーとして表示されるケースで、v2.1.222で「実は応答自体は完了していたのに誤表示されていた」バグが修正されている。症状が似ていて混同しやすいので、まずどちらのメッセージが出ているか(何も出ていないか、エラーとして出ているか)を確認するのが切り分けの第一歩になる。

バックグラウンド実行を使わなければ安全?
安全度は上がるが根絶はできない。フォアグラウンド実行でもパターン①(IPCチャネルのハング)は起きうる。ただしバックグラウンド特有のパターン③(完了通知の握りつぶし)は避けられるので、ネストしたサブエージェントを多用する設計なら、まずバックグラウンド実行を減らす方向で様子を見る価値はある。
サブエージェントが固まる問題は、根本的には「並行処理を任せている以上、どこかの通信が本当に切れたときの回復手段がない」という設計上の穴に起因している。Anthropic側もIssueを追跡してはいるものの、2026年8月時点でタイムアウト機能自体はまだ実装されていない。当面はここで挙げた3パターンの切り分けと、並列度・ネストを絞る予防策で被害を最小化するのが現実的な付き合い方になる。
Claude Code自体は更新頻度が高いツールなので、この記事の内容も数ヶ月後には状況が変わっている可能性がある。最新の挙動は公式のサブエージェントリファレンスで確認しておくといい。