CLAUDE_CODE_STOP_HOOK_BLOCK_CAPとは — Stop hookの無限ブロックを止める環境変数
CLAUDE_CODE_STOP_HOOK_BLOCK_CAPは、Stop/SubagentStop hookが会話終了を連続でブロックできる回数の上限を決める環境変数です。既定は8回、0で無効化できます。
CLAUDE_CODE_STOP_HOOK_BLOCK_CAP は、StopフックとSubagentStopフックが会話の終了を連続でブロックできる回数に上限をかける環境変数です。既定値は8回で、達すると設定を無視して強制的にターンを終了します。0 を指定すれば上限そのものを無効化できます。フックの終了条件を書き間違えたときの、いわば最後の安全網です。設定自体は単純ですが、何を数え、いつ解除されるかを正確に知らないと使いどころを誤ります。
CLAUDE_CODE_STOP_HOOK_BLOCK_CAPが防ぐもの
StopフックとSubagentStopフックは、応答が終わるたびに発火します。フック側が decision: "block" を返すと、Claudeは応答を終えられずに継続します。継続後にもう一度Stopが発火し、フックがまた block を返せば、理屈のうえでは無限に繰り返せます。
Stopフックが発火するのはClaudeが自ら応答を終えたときだけで、ユーザーが操作を中断した場合は発火しません。APIエラーでターンが終わった場合はStopではなく、後述するStopFailureフックが発火します。CLAUDE_CODE_STOP_HOOK_BLOCK_CAP が関わるのは、あくまでStopフックとSubagentStopフックが実際に発火し、継続を要求し続けたケースに限られます。
これは仕様どおりの挙動です。テストが通るまで終わらせない、レポートを書き終えるまで終わらせない、といった正当な用途がある一方で、フックの判定条件が間違っていると、二度と満たされない条件を待ち続けるだけの無限ループになります。CLAUDE_CODE_STOP_HOOK_BLOCK_CAP は、この連続ブロックの回数を数え、既定の8回に達した時点でClaude Codeがフックの判断を上書きし、ターンを終わらせます。
Stopフックが継続を要求する2つの方法
継続を要求する方法は decision: "block" だけではありません。hookSpecificOutput.additionalContext で「テストを実行してから終えて」のような指示を返す方法もあります。
{
"hookSpecificOutput": {
"hookEventName": "Stop",
"additionalContext": "Please run the test suite before finishing"
}
}見た目の違いは大きく、block はトランスクリプトにフックエラーとして表示されるのに対し、additionalContext は「Stop hook feedback」という通常のフィードバックとして表示されます。エラー表示を避けたいなら additionalContext を使う理由になりますが、CLAUDE_CODE_STOP_HOOK_BLOCK_CAP から見ればどちらも同じ「継続」です。stop_hook_active の付与も連続回数のカウントも共通で、additionalContext を使えばカウントされないという抜け道はありません。
判定結果の返し方も2通りあります。標準出力に decision と reason を含むJSONを書く方法と、終了コード2でスクリプトを終わらせ、標準エラーにメッセージを書く方法です。後者の場合、標準エラーのテキストがそのまま reason の代わりとしてClaudeに渡り、動作そのものに違いはありません。どちらの書き方を選んでも、連続ブロックの数え方は同じです。
Stop入力のbackground_tasksとsession_cronsで無駄な継続を減らす
Stop hookへの入力には stop_hook_active のほかに、background_tasks と session_crons という2つの配列も渡されます。background_tasks はまだ実行中のシェルコマンドやサブエージェント、MCPタスクなどを表し、session_crons はセッションに登録された未来の実行予定を表します。
フックがこれらを見ずに「応答が終わった」という理由だけでブロックを続けると、実際にはバックグラウンドタスクの完了を待っているだけのセッションに対して無駄な継続を発生させ、CLAUDE_CODE_STOP_HOOK_BLOCK_CAP の残り回数を消費してしまいます。background_tasks が空でなければ「セッションはまだ作業中」、両方が空なら「本当に何もすることがない」と区別できます。この2つのフィールドを判定に組み込むかどうかで、同じフックでも上限に達するまでの猶予が変わります。
設定のしかた
シェルの環境変数として渡すか、settings.json の env キーに書きます。この変数に対応するCLIフラグや設定キーは無く、環境変数だけで制御します。
export CLAUDE_CODE_STOP_HOOK_BLOCK_CAP=15
claudeチームで同じ上限を共有するなら、プロジェクトの設定ファイルに書いたほうが再現性があります。
{
"env": {
"CLAUDE_CODE_STOP_HOOK_BLOCK_CAP": "15"
}
}具体例 — テストが通るまで継続させるStopフック
Stopイベントはマッチャーを持たないため、matcher を書かずに登録します。応答が終わるたびに必ず発火し、スクリプトが判定を行う構成です。
{
"hooks": {
"Stop": [
{
"hooks": [
{
"type": "command",
"command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/require-green-tests.sh"
}
]
}
]
}
}スクリプト側はテストを実行し、失敗していれば decision: "block" と、どのテストがなぜ落ちているかを reason に書いてClaudeを継続させます。成功していれば何も出力せずに終了コード0で終え、Claudeの終了をそのまま許可します。${CLAUDE_PROJECT_DIR} はプロジェクトルートを指す変数で、リポジトリの場所によらずスクリプトのパスを固定できます。
テストの母数が多いリポジトリでは、1回の継続で直せる失敗が1〜2件ということも珍しくありません。8回のブロックで打ち切られると、直し切る前にターンが終わってしまいます。こうした構成こそ、上限を引き上げる判断が必要になる典型です。
SubagentStopにも同じ上限がかかる
この変数はStopフックだけでなく、サブエージェントの完了時に発火するSubagentStopフックにも同じ数値が適用されます。SubagentStopフックへの入力には stop_hook_active に加えて agent_type や、サブエージェント自身のトランスクリプトを指す agent_transcript_path が渡されます。メインセッションの transcript_path とは別の、subagents/ 配下にあるサブエージェント専用のトランスクリプトです。どのサブエージェントがどの記録で継続しているかを区別できるため、複数のサブエージェントが同時に走っていても、フック側で取り違える心配はありません。仕組みと上限の数え方そのものはStopフックと共通です。
サブエージェントのfrontmatterに書いたStopもSubagentStop扱いになる
サブエージェントの定義には、frontmatterに直接フックを書く方法もあります。ここで Stop イベントを指定すると、Claude Codeはそのサブエージェントが動いている間だけ有効なフックとして登録し、イベント自体は SubagentStop に変換して発火させます。サブエージェントの外に置いた通常の Stop 設定とは別物ですが、CLAUDE_CODE_STOP_HOOK_BLOCK_CAP の対象という点は同じです。サブエージェントを多用する構成では、こちらの発火元を見落としがちなので注意します。
使い分け早見表 — 似た安全装置との違い
「回数で暴走を止める」役割の環境変数やフックはほかにもあり、対象も既定値も異なります。
| 名称 | 対象 | 数えるもの | 既定値 | 0を入れたとき |
|---|---|---|---|---|
CLAUDE_CODE_STOP_HOOK_BLOCK_CAP | 対象Stop / SubagentStop hook | 数えるもの連続ブロック回数 | 既定値8 | 0を入れたとき上限を無効化 |
CLAUDE_CODE_MAX_TURNS | 対象セッション全体 | 数えるもの応答・ツール呼び出しのターン数 | 既定値無制限 | 0を入れたとき起動時エラー |
| StopFailureフック | 対象APIエラーでの終了 | 数えるもの対象外(継続の仕組みを持たない) | 既定値— | 0を入れたとき— |
StopFailureフックはAPIエラーでターンが終わったときに発火する別のイベントで、Claude Codeはこのフックの出力と終了コードを読み捨てます。decision や additionalContext による継続の仕組みそのものが無いため、CLAUDE_CODE_STOP_HOOK_BLOCK_CAP の対象にもなりません。ログ送信や通知だけを行う用途に限られます。
CLAUDE_CODE_STOP_HOOK_BLOCK_CAP と CLAUDE_CODE_MAX_TURNS は独立した上限で、片方を上げてももう片方の効き方は変わりません。CLAUDE_CODE_MAX_TURNS はStopフックの有無にかかわらずセッション全体のターン数を数えるのに対し、CLAUDE_CODE_STOP_HOOK_BLOCK_CAP はStopフックが実際にブロックを返した回数だけを数えます。Stopフックを使っていないセッションには、後者はそもそも関与しません。
上限を引き上げたほうがよいケース
正当な理由で8回を超える継続が必要になる構成は珍しくありません。前述のテスト駆動のStopフックのほか、レポートの各セクションを1回のブロックで1つずつ埋めていくような、段階的に完成へ近づけるフックも同様です。
上限を引き上げる前に見ておきたいのは、数値そのものより、フックが本当に終了条件へ向かって進んでいるかどうかです。毎回同じ理由で block を返し続けているなら、上限を上げても解決にはならず、待たされる回数が増えるだけになります。フックのスクリプト側で reason の内容をログファイルに書き残しておくと、何回目の継続でどう判定が変わったかを後から追え、上限に達する前に条件の書き間違いへ気付きやすくなります。イベント一覧と設定方法はHooks完全ガイドにまとめています。
まとめ
CLAUDE_CODE_STOP_HOOK_BLOCK_CAP は既定の8回のまま使って支障がないケースがほとんどです。値を変える必要が出るのは、StopフックかSubagentStopフックが正当な理由で8回を超えて継続する構成を組んだとき、あるいはサブエージェントのfrontmatterに書いたStopが想定より早く打ち切られているときに限られます。上限を0にして無効化する前に、フックの終了条件が現実に満たされる設計になっているかを見直すほうが、無限ループを防ぐという変数本来の目的に合っています。Claude CodeでTDDを回す手順では、Stop hookを使った継続の実例を扱っています。