[Bug] PermissionRequest hook cannot deny permissions - PermissionRequest hook decision ignored
Motivation
Long running sessions get hung up on permission requests.
The goal is to create a hook that automatically denies permission requests after a timeout.
Bug Description
PermissionRequest hook cannot deny permissions
## Summary
The PermissionRequest hook is called but cannot actually deny permission requests. The hook executes and
returns values, but the interactive permission prompt still appears regardless of the hook's output.
## Environment
- Claude Code version: v2.1.12
- OS: macOS (Darwin 24.6.0)
- Shell: zsh
## Expected Behavior
According to documentation and DeepWiki:
- PermissionRequest hooks run "after initial rule-based filtering and wildcard matching, but before an
interactive permission prompt is displayed"
- The hook should be able to return
allow,deny, oraskdecisions - Returning
denyshould deny the permission without showing the interactive prompt
## Actual Behavior
The PermissionRequest hook is called and executes, but:
- The interactive permission prompt always appears regardless of hook output
- The hook's decision is ignored
- The user must still manually respond to the prompt
## Steps to Reproduce
- Create hook script at
~/.claude/hooks/permission-timeout.sh:
``bash``
#!/bin/bash
INPUT=$(cat)
echo "[$(date)] PermissionRequest: $INPUT" >> /tmp/claude-permissions.log
echo '{"hookSpecificOutput":{"permissionDecision":"deny"}}'
exit 0
- Make it executable:
chmod +x ~/.claude/hooks/permission-timeout.sh
- Add to
~/.claude/settings.json:
``json``
{
"hooks": {
"PermissionRequest": [
{
"hooks": [
{
"type": "command",
"command": "/path/to/permission-timeout.sh",
"timeout": 65000
}
]
}
]
}
}
- Start a fresh Claude Code session
- Ask Claude to run a command that requires permission (e.g.,
mkdir /tmp/test)
Expected: Command is denied without showing prompt
Actual: Interactive permission prompt appears
## Formats Tested
All of these were ignored by Claude Code:
| Format | Result |
|--------|--------|
| {"hookSpecificOutput":{"permissionDecision":"deny"}} | Ignored, prompt shown |
| {"decision":"deny"} | Ignored, prompt shown |
| {"deny":true} | Ignored, prompt shown |
| Plain text deny | Ignored, prompt shown |
| Exit code 2 | Ignored, prompt shown |
## Verification
The hook IS being called - log file shows entries:
````
[Mon Jan 19 18:57:41 WET 2026] PermissionRequest:
{"session_id":"...","hook_event_name":"PermissionRequest","tool_name":"Bash","tool_input":{"command":"mkdir
123"...}}
[Mon Jan 19 18:57:41 WET 2026] Immediate deny via hookSpecificOutput
## Comparison with PreToolUse
PreToolUsehook withexit 2does work to block tool execution- But PreToolUse runs AFTER permission is granted, so it blocks without showing the prompt at all
- This is different from the desired behavior of "show prompt, auto-deny after timeout"
## Use Case
The intended use case is to implement a permission request timeout:
- Show the interactive permission prompt
- If user doesn't respond within N seconds, auto-deny
- Prevent Claude Code from blocking indefinitely waiting for user input
This is useful for:
- Automated/CI environments
- Background agents
- Preventing hung sessions
## Suggested Fix
Either:
- Make PermissionRequest hooks actually respect the returned decision
- Add a built-in
permissionRequestTimeoutsetting - Document that PermissionRequest hooks are informational-only (if intentional)
## Related
- Changelog mentions PermissionRequest hook was added in v2.0.45
- Changelog mentions "PermissionDecision is exposed to hooks, including ask"
Environment Info
- Platform: darwin
- Terminal: zed
- Version: 2.1.12
- Feedback ID: 592870b7-0780-4469-ae21-3e2e4640260f
This issue has 6 comments on GitHub. Read the full discussion on GitHub ↗