[Bug] PreToolUse hook permissionDecision ignored when tool in allow list
Bug Description
Bug Report: PreToolUse hooks permissionDecision is completely ignored when Bash is in allow list
Summary
When "Bash" is in the allow list in settings.json, PreToolUse hooks returning {"hookSpecificOutput": {"permissionDecision": "ask"}} or {"hookSpecificOutput": {"permissionDecision": "deny"}} are completely ignored. The command executes regardless of the hook's decision.
Environment
- Claude Code CLI (not VS Code extension)
- Linux (Debian/Ubuntu)
- Settings location: ~/.claude/settings.json
Configuration
settings.json:
{
"permissions": {
"allow": ["Bash", "Read"]
},
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "/root/.claude/hooks/check-dangerous-bash.sh"
}
]
}
]
}
}
Hook script (check-dangerous-bash.sh):
#!/bin/bash
INPUT=$(cat)
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty')
if [[ "$COMMAND" == "git commit" ]]; then
echo '{"hookSpecificOutput": {"permissionDecision": "deny", "permissionDecisionReason": "Dangerous command blocked"}}'
exit 0
fi
exit 0
Steps to Reproduce
- Configure settings.json with allow: ["Bash"] and a PreToolUse hook for Bash
- Create a hook script that returns permissionDecision: "deny" for git commit commands
- Ask Claude to run git commit --dry-run -m "test"
- Observe that the command executes despite the hook returning "deny"
Expected Behavior
According to the documentation, hooks should be able to override permissions:
- permissionDecision: "ask" should prompt the user for confirmation
- permissionDecision: "deny" should block the command entirely
The hook IS being called (verified via debug logging), and it DOES return the correct JSON output, but Claude Code ignores it.
Actual Behavior
- Hook is called ✅
- Hook returns correct JSON with permissionDecision: "deny" ✅
- Claude Code ignores the hook output and executes the command anyway ❌
Debug Evidence
# Hook is being called (from debug log):
[Thu Jan 15 09:25:33 UTC 2026] Hook called
[Thu Jan 15 09:25:33 UTC 2026] Input: {"tool_input":{"command":"git commit --dry-run -m \"test\""}, ...}
# Hook returns correct output:
$ echo '{"tool_input": {"command": "git commit -m test"}}' | ./check-dangerous-bash.sh
{"hookSpecificOutput": {"permissionDecision": "deny", "permissionDecisionReason": "Dangerous command blocked"}}
Use Case
I want to:
- Allow all Bash commands by default (to avoid constant permission prompts for every variation of safe commands)
- BUT require confirmation (ask) for dangerous commands like git commit, git push, rm -rf, etc.
Currently this is impossible because:
- allow: ["Bash"] bypasses ask rules in permissions (documented as DENY > ALLOW > ASK priority)
- PreToolUse hooks with permissionDecision are also bypassed when Bash is in allow list
Related Issues
- #17159 - Bash command executed without asking for permission
- #16710 - git commit executes without permission prompt
- #13339 - VS Code Extension Ignores Hook permissionDecision: "ask"
- #4669 - permissionDecision: "deny" in PreToolUse hooks is ignored
Suggested Fix
Hooks should take precedence over allow rules. The evaluation order should be:
- Hooks (if hook returns a decision, use it)
- Deny rules
- Ask rules
- Allow rules
This would allow users to have permissive defaults while still enforcing safety checks via hooks.
Environment Info
- Platform: linux
- Terminal: tmux
- Version: 2.1.7
- Feedback ID: 65c0057d-1afa-42ae-9089-a672bc1f00b8
Errors
[{"error":"Error: Error parsing line in /root/.claude/projects/-root-projects-tradozor/05be4838-02e2-4f1c-b364-23105cf7110a.jsonl: SyntaxError: Unexpected token 'p', \"np4Z+02UzSJ\"... is not valid JSON\n at file:///root/.nvm/versions/node/v20.19.5/lib/node_modules/@anthropic-ai/claude-code/cli.js:74:79\n at Array.map (<anonymous>)\n at Fg (file:///root/.nvm/versions/node/v20.19.5/lib/node_modules/@anthropic-ai/claude-code/cli.js:74:26)\n at async Mp (file:///root/.nvm/versions/node/v20.19.5/lib/node_modules/@anthropic-ai/claude-code/cli.js:4697:1984)\n at async wP0 (file:///root/.nvm/versions/node/v20.19.5/lib/node_modules/@anthropic-ai/claude-code/cli.js:4697:3816)\n at async x$7 (file:///root/.nvm/versions/node/v20.19.5/lib/node_modules/@anthropic-ai/claude-code/cli.js:4696:2995)\n at async J8A
Note: Error logs were truncated.
This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗