[BUG] permissionDecision: "deny" in PreToolUse hooks is ignored - tools execute anyway
Issue Type: Bug
Description:
PreToolUse hooks that return "permissionDecision": "deny" are not preventing tool execution as documented. The hook executes correctly
and returns the proper JSON response, but Claude Code ignores the denial and executes the tool anyway.
Steps to Reproduce:
- Create a simple test hook that always denies:
.claude/hooks/test-deny-hook.sh
#!/bin/bash
echo '🧪 TEST DENY HOOK: Always denying this bash command' >&2
cat << 'EOF'
{
"permissionDecision": "deny",
"permissionDecisionReason": "This is a test hook that always denies bash commands to test if permission decisions work properly."
}
EOF
exit 0
- Add hook to .claude/settings.json:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "cd \"$(git rev-parse --show-toplevel)\" && .claude/hooks/test-deny-hook.sh"
}
]
}
]
}
}
- Run any bash command: ls -la
Expected Behavior:
The bash command should be blocked and not execute, with Claude receiving the permissionDecisionReason.
Actual Behavior:
The bash command executes normally despite the hook returning "permissionDecision": "deny".
Debug Output Shows:
- Hook executes successfully: [DEBUG] Hook command completed with status 0
- Hook returns correct JSON: "permissionDecision": "deny"
- Tool still executes: [DEBUG] Bash tool invoked with command: ls -la
Environment:
- Claude Code version: 1.0.62 (Claude Code)
- OS: macOS
- Hook configuration: Project-level .claude/settings.json
Documentation Reference:
According to the https://docs.anthropic.com/en/docs/claude-code/hooks, "permissionDecision": "deny" should "prevent the tool call from
executing" and the "permissionDecisionReason" should be "shown to Claude".
This issue has 6 comments on GitHub. Read the full discussion on GitHub ↗