Hook event 20 of 33

PermissionDenied

Runs only when auto mode has already denied a tool call, including denials where no classifier verdict exists.

Exit code veto
Exit code cannot block. The denial has already happened by the time the hook runs; only a JSON retry field changes anything, and only when a classifier verdict exists.
Matchers
Built-in or MCP tool names, exact or regex, the same form PreToolUse uses
if filter
Applies
Docs checked
2026-08-29

The refusal already happened

By the time your hook runs, the call is already blocked. Auto mode is the permission mode where a classifier, an automatic judge of allow or refuse, reviews each action instead of you. Module 11 covers the modes. When that classifier refuses a tool call, PermissionDenied fires, after the refusal. It does not fire for a denial you gave at a prompt, a denial a PreToolUse hook issued, or a call that matched a deny rule.

The matcher filters tool names and the if filter applies. The payload carries permission_mode, tool_name, tool_input, tool_use_id, and reason, the text explaining why auto mode refused.

One field, and when it is ignored

The only lever is hookSpecificOutput.retry: true. It tells the model it may try the call again. It does not reverse the denial. When the classifier produced no verdict, the field is ignored and the original rejection stands. retry: false, or no JSON, leaves the rejection message as it was.

Exit codes here decide nothing

Do not reach for exit 2 here. Exit 0 leaves the denial in place, and valid JSON may add the retry hint. Exit 1 is the same, because the hooks reference reads JSON on every code. Exit 2 and its stderr are ignored outright. The denial already occurred, and there is no code that un-occurs it.

The mistake most people make first

You watch a denial, decide the classifier got it wrong, and return {"hookSpecificOutput": {"retry": true}} expecting the call to go through.

It does not. The denial stands. The field tells Claude it may try again, and it does nothing to make the second attempt succeed. Then the narrower case: some denials carry no classifier verdict, and on those retry is ignored outright. The same hook returning the same JSON has no effect, and nothing says so.

To let the call through, the mechanism is a permission rule in your settings, which module 11 covers. This event sits after the decision, and there is no field on it that changes the decision.