Claude Code の hooks の終了コード 0・2・その他の意味と使い分け
Claude Code の hooks スクリプトが返す終了コードによって、処理の続行・ブロック・警告がどう変わるかを解説します。終了コード 2 で標準エラー出力を Claude にフィードバックする仕組み、イベントごとの挙動の違い、JSON 出力による細かい制御を紹介します。
目次
hooks スクリプトの終了コードは、Claude Code に「続けてよいか」「止めるか」「注意だけするか」を伝える信号です。ここを理解していないと、lint が失敗しても Claude が気付かない、あるいは逆に警告のつもりが処理を止めてしまう、といった食い違いが起きます。
結論として、**0 は成功、2 はブロック(標準エラー出力が Claude に渡る)、それ以外は警告(ユーザーに表示、処理は続行)**です。
KEY POINT
この記事で分かること
- 終了コード 0 / 2 / その他の挙動の違い
- イベント(PreToolUse、PostToolUse など)ごとの「ブロック」の意味
- JSON 出力による細かい制御
終了コードの一覧
| 終了コード | 扱い | 標準出力 | 標準エラー出力 |
|---|---|---|---|
0 | 成功 | ユーザーに表示される(一部のイベントでは Claude のコンテキストに追加) | 表示されない |
2 | ブロック | 無視される | Claude に渡され、Claude はその内容に従って対応する |
その他(1 など) | 非ブロックのエラー | 無視される | ユーザーに表示される。処理は続行 |
「Claude に何かを伝えたい」ときは 2 を使います。「人間にだけ知らせたい」ときは 1 です。
イベントごとの「ブロック」の意味
終了コード 2 の効果は、どのイベントで返したかで変わります。
| イベント | 終了コード 2 の効果 |
|---|---|
PreToolUse | ツールの実行を中止する。標準エラー出力の内容が Claude に渡る |
PostToolUse | ツールは実行済み。標準エラー出力の内容が Claude に渡り、修正を促す |
UserPromptSubmit | プロンプトを Claude に渡さず、標準エラー出力の内容をユーザーに表示 |
Stop / SubagentStop | 停止を止め、Claude に作業を続けさせる(標準エラー出力が指示になる) |
Notification | 効果なし(通知は既に発生している) |
PreCompact | 効果なし |
用語解説
PostToolUse で「ブロック」しても取り消せない: PostToolUse は実行後に走るため、ファイルは既に書き換わっています。2 を返す目的は「取り消し」ではなく、「lint エラーがあるから直して」と Claude に伝えることです。
使い分けの例
PreToolUse で危険なコマンドを止める(2)
#!/usr/bin/env bash
cmd=$(jq -r '.tool_input.command // empty')
if echo "$cmd" | grep -Eq 'rm -rf /|git push --force'; then
echo "このコマンドは禁止されています: $cmd" >&2
exit 2
fi
exit 0
PostToolUse で lint 失敗を Claude に直させる(2)
#!/usr/bin/env bash
file=$(jq -r '.tool_input.file_path // empty')
[[ "$file" == *.ts ]] || exit 0
if ! npx eslint "$file"; then
echo "ESLint がエラーを報告しました。修正してください: $file" >&2
exit 2
fi
exit 0
人間にだけ知らせる(1)
#!/usr/bin/env bash
file=$(jq -r '.tool_input.file_path // empty')
if [[ "$file" == */migrations/* ]]; then
echo "注意: マイグレーションファイルが変更されました: $file" >&2
exit 1
fi
exit 0
Claude の処理は止まらず、ユーザーの画面に警告だけが出ます。
JSON 出力で細かく制御する
終了コード 0 で、標準出力に JSON を書くと、より細かい制御ができます。
{
"decision": "block",
"reason": "テストファイルは実装フェーズでは変更できません"
}
decision を block にすると終了コード 2 と同じくブロックされ、reason が Claude に渡ります。イベントによっては continue: false で Claude の処理全体を止めたり、追加のコンテキストを注入したりできます。対応するキーはイベントごとに異なるため、公式のリファレンスで確認してください。
標準出力に余計なものを出さない
JSON 制御を使う場合、標準出力に JSON 以外の文字列(デバッグ用の echo など)が混ざるとパースに失敗します。デバッグ出力は標準エラー出力に書いてください。
関連する記事
hooks の設定ファイルの書き方と、lint / format の自動実行の全体像は、親記事の hooks で lint と format を自動実行する を参照してください。
まとめ
- 終了コード 0 は成功、2 はブロック(標準エラー出力が Claude に渡る)、その他は警告(ユーザーに表示)
- PreToolUse の 2 は実行を中止、PostToolUse の 2 は実行済みの操作に対する修正指示になる
- 人間にだけ知らせたいときは 1、Claude に直させたいときは 2
- 標準出力の JSON で
decisionやreasonを返すと細かい制御ができる
よくある質問
- hooks で終了コード 1 を返すとどうなりますか?
- 0 と 2 以外の終了コードは「非ブロックのエラー」として扱われ、標準エラー出力がユーザーに表示されますが、Claude の処理は続行されます。
- PostToolUse で終了コード 2 を返しても操作は取り消せますか?
- 取り消せません。PostToolUse はツール実行後に走るため、2 を返しても実行済みの操作は残ります。標準エラー出力の内容が Claude に渡され、修正を促す形になります。
参考にした一次情報
この記事は公式ドキュメントを基に AI が下書きを作成し、運営者が内容を確認して公開しています。誤りを見つけた場合はお問い合わせからお知らせください。