Preflight Checklist
Summary
When --dangerously-skip-permissions is active and PreToolUse hooks are configured in settings.json, the permission mode resets to normal mid-session — suddenly requiring manual approval for every tool call. The session cannot recover without restart.
Related Issues
Environment
- Claude Code Version: 2.1.81
- Platform: macOS (Darwin 25.3.0)
- Shell: zsh
- Node.js: 22.x
Steps to Reproduce
- Configure PreToolUse hooks in
~/.claude/settings.json:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [{
"type": "command",
"command": "node \".claude/qk/hooks/dangerous-command-blocker.cjs\"",
"timeout": 5
}]
},
{
"matcher": "Write|Edit|MultiEdit",
"hooks": [{
"type": "command",
"command": "node \".claude/qk/hooks/file-boundary-guard.cjs\"",
"timeout": 5
}]
}
]
}
}
- Start Claude Code:
claude --dangerously-skip-permissions
- Work normally — tools execute without prompts
- After some time (varies: 30min–2hrs), permission prompts suddenly appear for ALL tool calls
- Even previously auto-approved tools (Read, Glob, Grep) now require confirmation
- Session is unrecoverable — must restart
Hook Output Protocol (verified correct)
Our hooks follow the documented protocol:
// Allow: silent exit
process.exit(0);
// Block (ask user): stdout JSON + exit 0
process.stdout.write(JSON.stringify({
hookSpecificOutput: {
hookEventName: 'PreToolUse',
permissionDecision: 'ask',
permissionDecisionReason: '...',
},
}));
process.exit(0);
- ✅ Always
exit(0) — never exit(2)
- ✅ Always
stdout — never stderr
- ✅ Outer try/catch wraps entire hook → fail-open on crash
- ✅ TTY detection → skip if no pipe (prevents blocking)
- ✅ Hooks only fire on matching tools (Bash for blocker, Write/Edit for guard)
What We've Tried
| Attempt |
Result |
Changed exit(2) + stderr → exit(0) + stdout JSON |
Improved but not fixed |
Changed permissionDecision: 'deny' → 'ask' |
No change |
Added hookEventName: 'PreToolUse' |
No change |
| Added fail-open crash wrapper |
No change |
| Set hook timeout to 5s |
No change |
Disabled hooks (qkit guard off) |
✅ Fixes it — confirms hooks are the trigger |
Key Observation
Disabling PreToolUse hooks completely prevents the issue. This confirms the permission reset is triggered by hook execution, not by other session factors.
Expected Behavior
--dangerously-skip-permissions should persist for the entire session regardless of PreToolUse hook output. Hooks returning permissionDecision: 'ask' should prompt for that specific tool call only, without resetting the global permission mode.
Actual Behavior
At some point during the session, the global permission mode resets from "bypass" to "normal", requiring approval for ALL subsequent tool calls — including tools that have no PreToolUse hooks configured.
Questions for the Team
- Does
permissionDecision: 'ask' from a PreToolUse hook interact with --dangerously-skip-permissions state? Is this by design?
- Is there a known race condition where hook timeout (5s) could cause permission state corruption?
- Should PreToolUse hooks be completely ignored when
--dangerously-skip-permissions is active?
Preflight Checklist
Summary
When
--dangerously-skip-permissionsis active and PreToolUse hooks are configured insettings.json, the permission mode resets to normal mid-session — suddenly requiring manual approval for every tool call. The session cannot recover without restart.Related Issues
Environment
Steps to Reproduce
~/.claude/settings.json:{ "hooks": { "PreToolUse": [ { "matcher": "Bash", "hooks": [{ "type": "command", "command": "node \".claude/qk/hooks/dangerous-command-blocker.cjs\"", "timeout": 5 }] }, { "matcher": "Write|Edit|MultiEdit", "hooks": [{ "type": "command", "command": "node \".claude/qk/hooks/file-boundary-guard.cjs\"", "timeout": 5 }] } ] } }claude --dangerously-skip-permissionsHook Output Protocol (verified correct)
Our hooks follow the documented protocol:
exit(0)— neverexit(2)stdout— neverstderrWhat We've Tried
exit(2)+ stderr →exit(0)+ stdout JSONpermissionDecision: 'deny'→'ask'hookEventName: 'PreToolUse'qkit guard off)Key Observation
Disabling PreToolUse hooks completely prevents the issue. This confirms the permission reset is triggered by hook execution, not by other session factors.
Expected Behavior
--dangerously-skip-permissionsshould persist for the entire session regardless of PreToolUse hook output. Hooks returningpermissionDecision: 'ask'should prompt for that specific tool call only, without resetting the global permission mode.Actual Behavior
At some point during the session, the global permission mode resets from "bypass" to "normal", requiring approval for ALL subsequent tool calls — including tools that have no PreToolUse hooks configured.
Questions for the Team
permissionDecision: 'ask'from a PreToolUse hook interact with--dangerously-skip-permissionsstate? Is this by design?--dangerously-skip-permissionsis active?