A Claude Code hook is a shell command that runs at a set point in the session, such as before a tool call. A PreToolUse hook that exits with code 2 blocks the action and sends its stderr message back. Exit 0 does not approve anything, and other exit codes usually let the action proceed.
Why hooks matter
Anthropic's hooks guide frames hooks as deterministic control: certain actions always happen, rather than depending on the model to choose to run them. The memory documentation makes the contrast sharp. CLAUDE.md is context. To block an action regardless of what Claude decides, use a PreToolUse hook.
Events you will use
The guide lists many events. The common ones:
- SessionStart: when a session begins or resumes.
- UserPromptSubmit: when a prompt is submitted, before Claude processes it.
- PreToolUse: before a tool call executes, and it can block it.
- PostToolUse: after a tool call succeeds, good for formatters.
- Stop: when Claude finishes responding.
Hooks live in a settings file, so you can scope them to a user, a project or an organization.
Exit codes
Hooks talk to Claude Code through stdin, stdout, stderr and exit codes. Event data arrives as JSON on stdin, including the session id and working directory. A blocking script looks like this:
#!/bin/bash
input=$(cat)
cmd=$(echo "$input" | jq -r '.tool_input.command')
if echo "$cmd" | grep -q 'rm -rf'; then
echo "Blocked: destructive delete" >&2
exit 2
fi
exit 0
Per the guide:
- Exit 0: no objection. For PreToolUse this does not approve the call. The normal permission flow still applies.
- Exit 2: Claude Code blocks the action, and the stderr reason goes to Claude as feedback for some events and to you for others.
- Any other code: for most events the action proceeds and the failure is reported as a non-blocking error.
That last rule is a trap. A script that crashes does not block anything unless you configured the hook as one whose failure blocks the action.
Several hooks on one event
When multiple hooks match, every one runs to completion before results merge. One hook returning deny does not stop sibling hooks from executing, so do not rely on a deny to suppress side effects in another hook. For PreToolUse decisions, the most restrictive answer wins, in the order deny, defer, ask, allow.
Limits to remember
Shell commands can also change files, so a hook matching only edit tools will not see everything. The guide suggests a Stop hook that scans the working tree once per turn if you need every change for compliance or audit.
Hooks run with your user permissions, so review them like any script. Organizations can audit or block settings changes during a session with ConfigChange hooks.
Quick check
Does a PreToolUse hook that exits 0 approve the tool call?
No. The normal permission flow still applies.
What if my blocking script crashes with exit code 1?
For most events the action proceeds as a non-blocking error. Use exit 2 to block.
Sources
Last reviewed: 19 October 2026.