Skip to content

PermissionRequest hooks never fire in --print mode on 2.1.237, while a PreToolUse control in the same settings file does #88328

Description

@voyagi

Summary

In --print mode, a PermissionRequest hook is never invoked, even when a tool call
plainly requires a permission decision and the model visibly stalls waiting for one.

A PreToolUse hook declared in the same settings file, matching the same tool, backed
by the same script, fires normally in the same run. So the settings file loads, the matcher
matches, the command quoting is valid, and the script is executable. The only variable that
differs between the two arms is the hook event.

This makes PermissionRequest unusable for its documented purpose, which is letting automation
answer a permission prompt in a non-interactive session. The consequence is worse than a hook
that errors: because the hook is never called, a -p run simply hangs on the prompt and then
gives up, and nothing anywhere reports that a configured hook was skipped.

Environment

  • Claude Code 2.1.237 (npm global install)
  • Windows 11 Pro, build 26200, AMD64
  • Node v22.22.0, npm 11.19.0
  • Reproduced with --model haiku; the model is not relevant to the failure

Reproduction

Three files in an empty directory. This is the exact repro that was run, with relative paths,
not a cleaned-up approximation.

hook.js

const fs = require('fs');
let raw = '';
process.stdin.on('data', c => raw += c);
process.stdin.on('end', () => {
  const label = process.argv[2];
  let p = {}; try { p = JSON.parse(raw); } catch {}
  fs.appendFileSync('marker.log', `${label} fired: event=${p.hook_event_name} tool=${p.tool_name}\n`);
  if (label === 'PERMISSIONREQUEST') {
    process.stdout.write(JSON.stringify({
      hookSpecificOutput: { hookEventName: 'PermissionRequest', decision: 'allow' }
    }));
  }
  process.exit(0);
});

settings.json

{
  "hooks": {
    "PreToolUse": [
      { "matcher": "Write", "hooks": [
        { "type": "command", "command": "node hook.js PRETOOLUSE", "timeout": 20 } ] }
    ],
    "PermissionRequest": [
      { "matcher": "Write", "hooks": [
        { "type": "command", "command": "node hook.js PERMISSIONREQUEST", "timeout": 20 } ] }
    ]
  }
}

Run:

claude -p "Use the Write tool to create hello.txt containing exactly: hi" \
  --permission-mode default --settings ./settings.json --model haiku

Expected

marker.log contains two lines. The PermissionRequest hook returns decision: "allow",
so hello.txt is written and the run completes.

Actual

marker.log contains exactly one line:

PRETOOLUSE fired: event=PreToolUse tool=Write

hello.txt is never created. The assistant's final output is a request for approval that
nobody can answer in -p mode, for example:

I'm ready to write hello.txt with the content "hi". Please approve the file write when prompted.

Why the control matters

The PreToolUse arm is not incidental. It is the same script file, invoked the same way,
differing only in an argv label, declared in the same settings.json. Its firing rules out
the ordinary explanations for a silent hook: settings file not loaded, matcher mismatch,
command-quoting problem, script not executable, working-directory problem, output-schema
problem. What remains is the event itself.

Second, independent data point

The same machine also has an unrelated PermissionRequest hook of type: "http" configured at
user scope, which logs on every approval it grants. Re-running the test against a path that
hook is specifically written to match produced no log entry from it either. So the failure
is not specific to hooks supplied via --settings, and not specific to type: "command".

Scope and limits, stated explicitly

  • Everything above is --print mode. An interactive TUI session was not tested, so a
    print-mode-specific code path is not ruled out. If PermissionRequest does fire
    interactively, then the correct reading of this report is narrower: the event is skipped on
    the non-interactive path, which is precisely the path where a hook-supplied decision is the
    only way a prompt can ever be answered.
  • The same machine has one historical log line showing this hook event firing successfully at
    an earlier date, so the event is not permanently dead here.
  • The host settings directory was not empty during the test. A fully isolated config directory
    was attempted and abandoned because it is unauthenticated. The control above is what stands
    in for that isolation, and it is the reason the control exists.

Relationship to existing issues

Suggested fix direction

Whatever dispatches the permission decision in non-interactive mode should call the
PermissionRequest chain before falling back to "cannot prompt". Failing that, a session that
skips a configured hook event should say so on stderr, so the failure is visible rather than
presenting as a stalled prompt.

Activity

  1. voyagi commented on Sep 10, 2026

    @voyagi
    Author

    Still reproducing on 2.1.267 (Windows 11, Node v24.19.0), using the exact three-file repro in the issue body, unchanged.

    marker.log after the run contains one line:

    PRETOOLUSE fired: event=PreToolUse tool=Write
    

    hello.txt is not created, and the run ends with "Permission needed to write the file. Please approve the write operation when prompted."

    The PreToolUse control still fires from the same script and the same settings file, so the settings load and the matcher matches. The documented output shape for the hook (hookSpecificOutput with hookEventName: "PermissionRequest" and decision: "allow") is also unchanged, so this is not schema drift on my side.

    Could this be reopened, or could someone say what "completed" referred to? No commit, PR, or changelog entry appears to be linked to it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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