
Exit Code 2 Blocks the Tool: PreToolUse Hooks as Deterministic Safety Gates in Claude Code
Chris Harper
2 min read
Aug 24, 2026 · 12:09 UTC
TL;DR: A PreToolUse hook exits with code 2 to block any Claude Code tool call before it runs — making your safety rules deterministic rather than relying on Claude's judgment call.
The PostToolUse validation hook (covered August 21) reacts after a write succeeds. PreToolUse intercepts before — useful for cases where you cannot let the action happen at all.
How blocking works
Exit code 2 from a PreToolUse hook blocks the tool call. Claude sees the hook's stdout and adjusts. Exit code 0 allows it. Any other non-zero exit also blocks, but code 2 gives Claude the clearest signal — use it.
Three guard patterns worth wiring
1. Block destructive shell commands. Intercept the Bash tool and reject commands matching rm -rf, git reset --hard, or DROP TABLE:
"hooks": {
"PreToolUse": [{
"matcher": "Bash",
"hooks": [{"type": "command",
"command": "bash .claude/hooks/danger-guard.sh"}]
}]
}
In danger-guard.sh: read $CLAUDE_TOOL_INPUT (JSON with the command field), grep for dangerous patterns, exit 2 if any match.
2. Block writes to generated files. Intercept Edit and Write, check the path against a deny list (public/blog/posts.json, dist/, out/), and exit 2 if the file is off-limits. This blog uses exactly this pattern — validate-blog.sh fires on every write to public/blog/ and blocks inconsistent writes.
3. Auto-approve read-only commands. Exit 0 immediately for cat, ls, grep, git status, and git log — no permission prompt, no approval fatigue.
With --dangerously-skip-permissions: PreToolUse hooks still fire in this mode. They become the last remaining guardrail when permission prompts are disabled — add your danger-guard before enabling that flag in CI or in unattended workflows.
The one gap: Hooks do not fire inside Workflow SDK subagents. If your workflow calls an agent() step that writes files, the PreToolUse hooks in your main session do not cover it. Add validation as an explicit agent() step in the script instead.
Sources: Hooks reference — code.claude.com · Claude Code Hooks: Security Gates for Agent Workflows — DEV Community · PreToolUse hooks with --dangerously-skip-permissions — pasqualepillitteri.it