Skip to content

[BUG] --dangerously-skip-permissions resets mid-session when PreToolUse hooks are configured #37745

Description

@backviet

Preflight Checklist

  • I have searched existing issues and this hasn't been reported yet
  • This is a single bug report (not multiple bugs)
  • I am using the latest version of Claude Code

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

  1. 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
        }]
      }
    ]
  }
}
  1. Start Claude Code: claude --dangerously-skip-permissions
  2. Work normally — tools execute without prompts
  3. After some time (varies: 30min–2hrs), permission prompts suddenly appear for ALL tool calls
  4. Even previously auto-approved tools (Read, Glob, Grep) now require confirmation
  5. 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

  1. Does permissionDecision: 'ask' from a PreToolUse hook interact with --dangerously-skip-permissions state? Is this by design?
  2. Is there a known race condition where hook timeout (5s) could cause permission state corruption?
  3. Should PreToolUse hooks be completely ignored when --dangerously-skip-permissions is active?

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions