[BUG] permissionDecision: "deny" in PreToolUse hooks is ignored - tools execute anyway

Resolved 💬 6 comments Opened Jul 29, 2025 by a-c-m Closed Jan 5, 2026

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:

  1. 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

  1. 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"
}
]
}
]
}
}

  1. 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".

View original on GitHub ↗

This issue has 6 comments on GitHub. Read the full discussion on GitHub ↗