Claude Code Hooks Can Block Risky Actions
Claude Code hooks run shell commands at lifecycle points and can deterministically block tool calls like risky edits or shell commands.
Claude Code Hooks Can Block Risky Actions
Claude Code includes a hooks system that runs user-defined shell commands at specific points in its lifecycle. According to the official hooks reference at https://code.claude.com/docs/en/hooks, hooks provide deterministic control over Claude Code’s behavior, ensuring certain actions always happen rather than relying on the model to choose to run them. This makes hooks a practical place to enforce project rules, including security and safety checks, before changes take effect.
How Hooks Fire
The documentation organizes hook events into three cadences. Some run once per session, such as SessionStart, SessionEnd, and Setup. Others run once per turn, including UserPromptSubmit, Stop, and StopFailure. A third group runs on every tool call: PreToolUse, PostToolUse, PostToolUseFailure, and PostToolBatch.
For a check that needs to run before code changes are applied, the relevant event is PreToolUse, which fires before a tool call executes and can block it. The reference lists many other events as well, covering permissions, subagents, tasks, compaction, worktrees, and configuration changes.
Blocking With Exit Codes
Hooks signal results in one of two mutually exclusive ways: exit codes alone, or exit code 0 with JSON output. The two cannot be combined.
For exit codes, the reference defines three outcomes. Exit code 0 means success, and stdout is parsed for JSON output fields. Exit code 2 is a blocking error: stdout and JSON are ignored, and stderr is fed back to Claude as an error message. Any other exit code is treated as a non-blocking error, so execution continues.
A notable detail is that exit code 1 does not block. Even though 1 is the conventional Unix failure code, Claude Code treats it as a non-blocking error that proceeds anyway. To enforce policy, the documentation says to use exit code 2. The one exception is the WorktreeCreate event, where any non-zero exit code aborts creation.
A Validator Example
The reference includes an example that blocks destructive shell commands. A PreToolUse hook matching the Bash tool runs a handler script that reads the proposed command and inspects it. In the exit-code version of the handler, the script checks whether the command starts with rm, prints a message to stderr, and calls exit 2 to prevent the tool call. If the command is allowed, it calls exit 0.
For finer-grained control, a hook can instead exit 0 and print JSON. The PreToolUse event uses hookSpecificOutput.permissionDecision, which accepts values such as allow, deny, ask, and defer. Universal JSON fields include continue, stopReason, systemMessage, and additionalContext.
Guidance Worth Noting
The documentation is direct about the limits of hooks as a security control. The if filter that decides whether a hook runs is best-effort and fails open, running the hook anyway when a Bash command cannot be parsed. Because of this, the docs recommend using the permission system rather than a hook to enforce a hard allow or deny. The reference also warns that a silent hook does not approve an action; it only allows the normal permission flow to continue.
Other notes include that Bash matching inspects subcommands inside $() and backticks, that administrators can restrict hooks through managed settings, and that hook output strings are capped at 10,000 characters. For teams that want a starting point, Anthropic links a Bash command validator reference implementation in the claude-code examples directory.
Source: code.claude.com
Related Tips
Memory Systems for Long-Running AI Agents
How long-running AI agents manage memory through compaction, note-taking, and sub-agents, based on Anthropic's context engineering guidance.
How Claude Code Remembers Projects With CLAUDE.md
Claude Code uses CLAUDE.md files and auto memory to carry project context across sessions, with files written by developers and notes written by Claude.
Run Claude Code From Discord With a Bot Bridge
An open-source Discord bot runs Claude Code from chat channels, with sandboxing, role-based access, MCP management, and Git branch organization.