CLAUDE LABEN
FORK — Claude Code 2.1.212で/forkの挙動が変わりました。会話を新しいバックグラウンドセッションへ複製し、作業を続けたまま並走できます。従来のセッション内サブエージェントは/subtaskに移りましたLIMITS — WebSearchの呼び出しがセッション単位で既定200回に制限されました。サブエージェントの起動も既定200回が上限で、暴走した検索・委譲のループを止められますMCPBG — 2分を超えるMCPツール呼び出しは自動的にバックグラウンドへ移り、セッションが固まらなくなりました。しきい値はCLAUDE_CODE_MCP_AUTO_BACKGROUND_MSで調整できますPLANFIX — プランモードがtouchやrmといったファイルを変更するBashコマンドを、許可プロンプトもcanUseToolコールバックも通さずに実行してしまう不具合が修正されましたSONNET5 — Claude Sonnet 5は導入価格として入力100万トークンあたり2ドル、出力10ドルで提供中です。8月31日を過ぎると3ドルと15ドルに戻りますIPO — Anthropicが早ければ10月の株式公開を視野に、引受銀行が投資家との面談を組み始めたと報じられていますFORK — Claude Code 2.1.212で/forkの挙動が変わりました。会話を新しいバックグラウンドセッションへ複製し、作業を続けたまま並走できます。従来のセッション内サブエージェントは/subtaskに移りましたLIMITS — WebSearchの呼び出しがセッション単位で既定200回に制限されました。サブエージェントの起動も既定200回が上限で、暴走した検索・委譲のループを止められますMCPBG — 2分を超えるMCPツール呼び出しは自動的にバックグラウンドへ移り、セッションが固まらなくなりました。しきい値はCLAUDE_CODE_MCP_AUTO_BACKGROUND_MSで調整できますPLANFIX — プランモードがtouchやrmといったファイルを変更するBashコマンドを、許可プロンプトもcanUseToolコールバックも通さずに実行してしまう不具合が修正されましたSONNET5 — Claude Sonnet 5は導入価格として入力100万トークンあたり2ドル、出力10ドルで提供中です。8月31日を過ぎると3ドルと15ドルに戻りますIPO — Anthropicが早ければ10月の株式公開を視野に、引受銀行が投資家との面談を組み始めたと報じられています
記事一覧/Claude Code
Claude Code/2026-04-25中級

Claude Code Hooksが無限ループする問題 — PostToolUseが自己発火するときの止め方

Claude Code の Hooks を仕掛けたら、ツール呼び出しが止まらなくなった。PostToolUse が自分で書き換えたファイルを再び拾って延々と発火し続ける、典型的な無限ループの原因と止め方を実例ベースで解説します。

claude-code129hooks14troubleshooting61automation54fix11

「Claude Code に prettier の自動整形を仕込んだら、Edit と PostToolUse が交互に発火し続けて、ターミナルが滝のように流れて止まらなくなった」 — Hooks をある程度使い込んでいる方なら、一度は経験したことがあるのではないでしょうか。私自身、最初に PostToolUse と PreToolUse を本格的に使ったときに、まさにこの状態に陥り、Ctrl+C を連打して状況を止めたことを今でも覚えています。

Hooks の無限ループは、ドキュメントには「気をつけてください」程度しか書かれていませんが、実際には設定ファイルを少し書き換えただけで誰でも踏める落とし穴です。ループが起きる仕組みを順を追って整理しつつ、私が普段使っている止め方のパターンを順番にご紹介します。

なぜ Hooks は無限ループするのか

PostToolUse は、Claude Code が何らかのツール(Edit / Write / Bash など)を実行した直後に呼ばれるイベントです。ここで「実行されたツールの結果に応じて、別のコマンドを叩く」という設計が自然にできます。問題は、その「叩いたコマンド」自体が、Claude から見て新たなツール呼び出しとして観測されたり、ファイル状態を変えてしまったりするときに起こります。

典型的なパターンは次の3つです。

  • 整形系の自動化が再発火を呼ぶ: Edit のあとに prettier --write を実行 → ファイルが書き換わる → Claude がその差分を見て「もう一度 Edit が必要」と判断 → さらに PostToolUse が発火、というループ。
  • git 系コマンドが新しい Bash 呼び出しに化ける: PostToolUse(Write) のフックの中で git add . && git commit を実行している場合、フック自身がシェル経由で動くため、ログ上は Bash 呼び出しに見えてしまい、これに反応する別の Bash 用 PostToolUse が二次発火する設計だと連鎖します。
  • マーカーファイルの更新が Edit 扱いになる: チェックポイントやログ用の .claude-last-run.json を書き出すフックを仕掛けると、その書き込みを別の監視系フックが拾ってさらに発火する、という同型の事故もよく見ます。

共通しているのは「フックの副作用が、フックの発火条件と被っている」という構造です。発火条件と副作用が重なる限り、論理的にループが止まる理由はありません。

まず止血する — 暴走しているフックを安全に切る

ループ中の Claude Code は、Ctrl+C 一発では止まらないことがあります。プロセスとしては動き続けて、画面だけが止まる、というケースもあります。私が最初にやることはいつも同じで、settings.json のフック定義を 物理的に空にする ことです。

# 一時的に hooks を空にして暴走を止める
cd ~/.claude
cp settings.json settings.json.bak
jq '.hooks = {}' settings.json > settings.json.tmp && mv settings.json.tmp settings.json

~/.claude/settings.json のほか、プロジェクト直下の .claude/settings.json.claude/settings.local.json にも hooks が書かれていることがあります。深いリポジトリだと複数のスコープが合成されるので、検索コマンドで一度に確認しておくと安心です。

grep -rln '"hooks"' ~/.claude .claude 2>/dev/null

止血したら、バックアップから定義を1行ずつ戻して、どのフックが暴走の起点だったかを特定します。

ループを設計レベルで防ぐ3つのパターン

止血のあとは、同じ事故を二度と起こさないための恒久対策です。私が実プロジェクトで使っているパターンを3つ紹介します。

パターン1: 「再発火マーカー」で1回限りに制限する

最もシンプルで効果が大きいのが、フック側に「自分が原因の変更には反応しない」フラグを持たせる方法です。環境変数 CLAUDE_HOOK_DEPTH を見て、すでにフックが起動中なら早期 return します。

#!/bin/bash
# .claude/hooks/format.sh — 整形フックの再発火を防ぐテンプレート
if [ -n "${CLAUDE_HOOK_DEPTH:-}" ]; then
  # フック内から呼ばれた処理は、もう一段ネストしない
  exit 0
fi
export CLAUDE_HOOK_DEPTH=1
 
# 本来の処理
prettier --write "$1" >/dev/null 2>&1
 
# 期待出力: 何も標準出力に出さない(Claude が再判断する材料を与えない)
exit 0

ポイントは「フック由来の変更は Claude に通知しない」ことです。フックから echo で結果を吐くと、Claude は「次のステップを考える材料」として読み込んでしまい、ループを誘発します。整形系のように 副作用が成功している限り報告不要 な処理は、stdout を空に保つだけでループ確率がぐっと下がります。

パターン2: matcher を絞ってフック自身の操作を除外する

Hooks 設定の matcher には正規表現が書けます。ここを丁寧に絞り込んでおくと、「フック内で実行した Bash」は対象外、といった選別が可能です。

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "command": ".claude/hooks/format.sh",
        "exclude_paths": [".claude-last-run.json", "node_modules/**", ".next/**"]
      },
      {
        "matcher": "Bash",
        "command": ".claude/hooks/log-bash.sh",
        "exclude_commands_matching": "^(git |prettier|eslint|jq )"
      }
    ]
  }
}

exclude_commands_matching のような除外条件は、暴走を防ぐと同時にログも見やすくなります。Hooks の挙動が想定と違う場合は、まず matcher の評価順を疑うのが基本です。詳細な発火条件の整理は Claude Code Hooksが発火しない・設定が反映されないときの原因と解決方法 に整理しているので、合わせて確認してみてください。

パターン3: ファイルロックで「同時に1つだけ」に制限する

最後の保険は、フックを flock で直列化することです。整形・コミット・通知のように、複数のフックが同じファイル群を触る場合、ロックを取って実行すれば多重発火しても順番待ちになるだけで、ループにはなりません。

#!/bin/bash
# .claude/hooks/safe-format.sh
LOCK_FILE="/tmp/claude-hooks.lock"
exec 9>"$LOCK_FILE"
if ! flock -n 9; then
  # 既に走っていれば諦める(=多重実行を許さない)
  exit 0
fi
 
prettier --write "$1" >/dev/null 2>&1
# 期待挙動: ロックは exit 時に自動解放

ロック方式は副作用の重複を許さない代わりに、長時間処理を握ると後続のフックが落ちます。私は整形のような数百ms単位の処理にだけ使い、長い処理にはマーカー方式(パターン1)を併用しています。

ループの兆候を早めに掴むためのログ設計

無限ループは、起きてから気づくのではなく「起きそうな兆候」のうちに気づきたい現象です。私はフック側に 連続発火回数 のカウンタを仕込み、ある閾値を超えたら自動で止まる仕組みにしています。

#!/bin/bash
# 連続発火を検知して自爆するフック
COUNT_FILE="/tmp/claude-hook-count"
NOW=$(date +%s)
LAST=$(cat "$COUNT_FILE.time" 2>/dev/null || echo 0)
COUNT=$(cat "$COUNT_FILE" 2>/dev/null || echo 0)
 
if [ $((NOW - LAST)) -lt 5 ]; then
  COUNT=$((COUNT + 1))
else
  COUNT=1
fi
echo "$COUNT" > "$COUNT_FILE"
echo "$NOW" > "$COUNT_FILE.time"
 
if [ "$COUNT" -ge 10 ]; then
  echo "⚠️ Hook が5秒間に10回連続発火しました。多重実行の疑いがあるためスキップします。" >&2
  exit 0
fi
 
# ここから本来の処理

このようなセーフティを入れておけば、設定ミスで多重発火しても、被害が「画面が少し騒がしい」程度で済みます。本番運用での Hooks のトラブルシューティングについては、Claude Code Hooks の本番デバッグ術 でさらに踏み込んだ可観測性の話をまとめています。

全体を振り返って — 今日できる一歩

無限ループを完全になくす特効薬はありません。ただ、「フックの副作用が、フックの発火条件と被らないように設計する」という一点を意識するだけで、事故率は劇的に下がります。今日まず手を動かすなら、次の3つのうちどれか1つだけで構いません。

  1. 既存の PostToolUse フックを開いて、stdout に何かを出していないか確認しましょう。整形系なら、出力は空にしてください。
  2. matcher の exclude_pathsexclude_commands_matching を1つ追加して、フック自身の副作用を除外しましょう。
  3. 5秒間で10回以上発火したら自動で止まる、安全装置スクリプトを .claude/hooks/ に1本置いておきましょう。

特に3つ目は、Hooks をこれから複雑にしていく予定があるなら、最初に入れておくべき土台だと考えています。Hooks 全体の設計を見直したくなったら、Claude Code Hooks 自動化マスターガイド の方も参考にしながら、ご自身のワークフローに合うパターンを選んでみてください。

シェア

お読みいただきありがとうございます

Claude Lab は広告なしで運営しており、サーバー費用などの運営コストはメンバーシップのご支援で賄っています。実装コード・ベンチマーク・本番設計パターンなど、実務でお役立ていただける記事を毎日更新しています。もし読んでよかったと感じていただけましたら、ぜひご覧ください。

  • コピー&ペーストで使える実装コード付き
  • 毎日新しい上級ガイドを追加
  • ¥580/月 または ¥1,480 の永久アクセス
メンバーシップを見る →

もしこの記事がお役に立ちましたら、チップ(¥150)で応援いただけると大変励みになります。広告なしでの運営を続けるため、皆さまのご支援が大きな力になっています。

関連記事

Claude Code2026-06-10
git add -A が .bak バックアップまで巻き込む — in-place 修正スクリプトと自動コミットの落とし穴
sed -i や自作の --fix スクリプトが残す .bak バックアップを git add -A が無言で取り込み、本番リポジトリに混入させてしまう問題。なぜ気づきにくいのか、scoped add と .gitignore でどう塞ぐかを実例で整理します。
Claude Code2026-06-09
前回の記事本文が別の記事に紛れ込む — 固定名の一時ファイルに残る残骸の罠
Claude Code の自動化で、固定名の一時ファイルに前回の中身が残り、まったく別の記事に古い本文が混入する。書き込みが無音で失敗する仕組みと、ユニーク名+挿入後の grep 照合で防ぐ方法を記録します。
Claude Code2026-06-03
Claude Code で git push が「成功」したのに反映されない — commit が無音で失敗する罠
git push がエラーを出さず Everything up-to-date と表示されるのに、変更がリモートに反映されない。clone 直後の identity 未設定で commit が無音失敗する原因と、SHA 照合で確実に検証する方法を解説します。
📚RECOMMENDED BOOKS
大規模言語モデル入門
山田育矢
LLM開発
生成AIプロンプトエンジニアリング入門
我妻幸長
プロンプト
Claude CodeによるAI駆動開発入門
平川知秀
AI駆動開発
※ アフィリエイトリンクを含みます
もっと見る →