Skip to content

Prove a Claude Code Hook Blocks Before You Trust It

2026-08-14

Say you write a PreToolUse hook so Claude Code can never edit data/test/, the held-out split your reported numbers depend on. The hook works on the case you tried. Does it hold on the cases you didn't?

The hooks reference is specific about how a hook fails:

  • For most events, exit code 2 is the only exit code that blocks through the code alone. Exit 1, the usual Unix failure code, is a non-blocking error, and the action proceeds.
  • A hook that can't start, such as a mistyped script path in settings.json, is also a non-blocking error; the docs warn that this leaves the gate silently disabled. A script whose helper, such as jq, is missing exits with some code other than 2 unless you make it, so the edit goes through too.
  • A command hook that reaches its timeout, 600 seconds by default, is canceled and its output discarded. On PreToolUse, the tool call then continues.

So a guard can fail open in at least three ways that still look like "the hook ran."

The check. Pipe four inputs into the script and print the exit code for each:

InputExpected exit
Edit to data/test/labels.csv2
Edit to src/train.py0
Edit to data/./test/labels.csv2
Edit to data/test/labels.csv with jq removed from PATHyour decision
bash
echo '{"tool_name":"Edit","tool_input":{"file_path":"data/test/labels.csv"}}' \
  | .claude/hooks/protect-test-split.sh; echo "exit $?"

The third row catches a string match on the path. The fourth forces a decision: if the helper is missing, should the guard refuse (exit 2) or let the edit through? Write that choice into the script instead of inheriting it. The same goes for anything that could run slowly: a guard that shells out to a network call can hit the timeout and let the edit through.

Keep the four fixtures next to the hook and rerun them whenever the script changes. Why a hook beats a line in CLAUDE.md, and what check to put behind one, is the subject of a companion note; this one is about the hook itself failing. For a guard this important, a model is the wrong enforcement layer and an untested script is only a little better. Chapter 15 of the book makes the same argument for tool boundaries: words steer the model, boundaries hold.

Claude Code documentation.