PreToolUse hook "ask" permissionDecision has no blocking effect in permission_mode: "auto"

Status Open
Maintainer reply None cached
Activity 0 comments · opened Aug 25, 2026

Summary

A PreToolUse hook that returns hookSpecificOutput.permissionDecision: "ask" has no blocking effect when the session's permission mode is auto (and likely other non-interactive modes such as bypassPermissions/dontAsk) — the tool call proceeds immediately, as if the hook had returned "allow". This contradicts the documented behavior in the hooks reference, which states unconditionally that an "ask" decision pauses the tool call and escalates to the user for manual approval.

Expected behavior

Per the hooks documentation, permissionDecision: "ask" should:

  1. Pause the tool call
  2. Prompt the user to manually approve or deny it
  3. Show the hook's permissionDecisionReason to the user

No carve-out for specific permission modes is documented.

Actual behavior

In permission_mode: "auto", a hook returning "ask" does not pause anything — the tool call (Edit/Write in our case) completes immediately with no error, no warning, no prompt. Verified via debug-log instrumentation of a real hook across two independent occurrences: the hook received the correct payload (including "permission_mode":"auto") and computed permissionDecision: "ask" with an accurate reason, but that decision had no effect on execution.

Since there is no interactive surface to route an "ask" decision to in auto mode, it appears to silently resolve to an implicit allow rather than a safe default (e.g., deny, or a non-blocking warning).

Repro sketch

  1. Register a PreToolUse hook (matcher Edit|Write) that always returns:

``json
{"hookSpecificOutput": {"hookEventName": "PreToolUse", "permissionDecision": "ask", "permissionDecisionReason": "test"}}
``

  1. Run a session with permission_mode: "auto".
  2. Trigger a matching Edit/Write tool call.
  3. Observe: the tool call completes without pausing, with no visible indication the hook's "ask" was ever surfaced.

Why this matters

For a hook author, "ask" and "deny" read as different risk postures — "ask" implies a human checkpoint, "deny" implies fail-closed. In interactive sessions that distinction holds. In auto/non-interactive sessions it silently collapses to "always allow," which is the opposite of what a cautious hook author would expect from a decision named "ask." We hit this in practice with a hook designed to catch edits accidentally landing on the wrong git worktree — it fired correctly and computed the right decision, but never actually blocked anything in auto mode, and the mistake it was built to prevent kept recurring. The fix was to switch the hook to "deny", but that required first discovering (via debug logging) that "ask" wasn't working as documented.

Ask

Either:

  • Document that "ask" has different behavior (or is unsupported/discouraged) in non-interactive permission modes, so hook authors know to use "deny" for anything safety-critical, or
  • Make "ask" fail closed (behave as "deny") when no interactive prompt surface is available, so the documented "pauses and escalates to the user" promise never silently degrades to "allow."

Environment

  • Claude Code CLI, Windows 11, PowerShell-based hooks
  • permission_mode: "auto"
  • Hook type: PreToolUse, matcher Edit|Write

View original on GitHub ↗