ClaudeHowSupport Us

How Claude Code hooks actually work

What a hook actually is

A hook is a shell command Claude Code runs automatically at a specific point in a session — before a tool executes, after it finishes, when a session starts, when Claude is about to stop and hand control back to you. It isn't a prompt instruction asking Claude to behave a certain way; it's a real process that runs outside the model entirely, which is the whole point. A hook enforces something deterministically that a system-prompt instruction can only ask nicely for.

Why that distinction matters in practice

Telling Claude in a system prompt "never run rm -rf" is a request the model usually honours and occasionally doesn't, especially under an unusual framing of the task. A hook that inspects the command about to run and blocks it outright doesn't have that failure mode — it either runs or it doesn't, and the model's compliance isn't part of the decision. That's the reason hooks exist alongside the permission system rather than instead of better prompting: some guarantees need to live outside the model's judgment entirely.

The points in a session a hook can attach to

Hooks fire at defined moments — before a tool call is about to run, immediately after one completes, when a session starts or resumes, and when Claude is about to stop and return control. Each one serves a different purpose: a pre-tool-call hook can inspect and block a command before it executes; a post-tool-call hook can react to what just happened, logging it or triggering a follow-up action; a session-start hook is the natural place to load project-specific context automatically rather than relying on Claude to ask for it.

A blocking hook versus an observing one

Not every hook needs to be able to stop something from happening. A hook that just logs every shell command a session runs, for an audit trail, doesn't need to block anything — it observes and lets the tool call proceed regardless of what it does with the information. A hook that checks whether a file path falls inside a protected directory before an edit is allowed to proceed is doing the opposite job entirely, and its exit behaviour is what decides whether the tool call goes ahead. Conflating the two is a common early mistake — write an observing hook when you actually needed a blocking one, and the thing you thought you'd prevented still happens; it just also gets logged.

Where hooks tend to go wrong

The most common failure isn't a hook doing the wrong thing — it's a hook that's slow, flaky, or that fails in a way that silently doesn't block what it was supposed to block. A hook that shells out to a slow external check on every single tool call adds real latency to a session that previously felt instant, and a hook script with an unhandled error can exit in a way Claude Code treats as "allow" rather than "deny," which is the opposite of what a security-oriented hook is usually meant to do. Test a blocking hook's failure path explicitly, not just its happy path — what happens when the check itself errors out matters as much as what happens when it correctly finds a problem.

Where hooks fit next to the permission system

Hooks and the permission system solve overlapping but distinct problems. Permissions are the built-in, declarative layer — this tool needs confirmation, that one is auto-allowed, this path is off-limits — and cover the common cases without writing any code. Hooks are for logic permissions can't express: a check that depends on the content of a command, not just which tool is being called; an integration with an external system; a project-specific rule that doesn't generalise. See configuring Claude Code permissions without fighting them for the layer hooks sit alongside, and reach for a hook specifically when a permission rule alone can't express the check you need.

Verified 2026-08-08 against ClaudeHow facts module (src/data/facts/) — see /about/#accuracy.