[BUG] UserPromptSubmit hook registered and valid but never invoked on real prompt submission (Git Bash CLI, Windows, v2.1.261) — possible regression of #17277

Status Open
Reported on v2.1.3
Maintainer reply None cached
Activity 1 comment · opened Sep 5, 2026

Title

UserPromptSubmit hook registered and valid, but never invoked on real interactive prompt submission (Git Bash CLI, Windows)

Environment

  • Claude Code version: v2.1.261
  • OS: Windows 10, running Claude Code inside Git Bash (MINGW64)
  • Model: Sonnet 5, medium effort
  • Permission mode: auto
  • Project: local project with .claude/settings.json defining a UserPromptSubmit hook and a PreToolUse hook (matcher: Bash)

Summary

A UserPromptSubmit hook configured in the project's .claude/settings.json is correctly listed by /hooks, and the hook script itself works perfectly when invoked manually with the exact same JSON payload Claude Code would send it — but it is never actually invoked when a real prompt is typed and submitted interactively in the CLI. The prompt reaches the model unmodified every time, across multiple fresh session restarts.

.claude/settings.json (relevant part)

{
  "hooks": {
    "UserPromptSubmit": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "node \"$CLAUDE_PROJECT_DIR/.claude/hooks/block-secrets.js\"",
            "shell": "bash"
          }
        ]
      }
    ]
  }
}

Steps to reproduce

  1. Create a UserPromptSubmit hook as above, pointing to a Node.js script that:
  • reads stdin JSON ({"prompt_text": "..."}),
  • detects a hardcoded-looking secret in prompt_text,
  • if found, writes a message to stderr and calls process.exit(2).
  1. Confirm the hook is registered: run /hooks inside the CLI session — it correctly lists the UserPromptSubmit entry pointing at the script, under "Project Settings".
  2. Confirm the script works standalone, run directly in the same shell:

``bash
node ".claude/hooks/block-secrets.js" <<< '{"prompt_text":"const apiKey = \"sk-ant-api03-abcdefghijklmnop1234567890\";"}'
echo "exit: $?"
`
Output: the expected "Blocked: detected potential credential(s)..." message, exit code
2`.

  1. Fully exit the claude process (/exit), start a brand-new session (claude) in the same project directory.
  2. Type the exact same string directly into the prompt: const apiKey = "sk-ant-api03-abcdefghijklmnop1234567890";

Expected

Per /hooks' own documented semantics for UserPromptSubmit:

Exit code 2 - block processing, erase original prompt, and show stderr to user only

The prompt should never reach the model; only the hook's stderr message should be shown to the user.

Actual

The model receives and responds to the raw prompt normally (a real assistant turn, with real token usage recorded), as if no hook were configured at all. No hook-block message is ever shown. This was verified directly by inspecting the session's .jsonl transcript in ~/.claude/projects/<project>/<session-id>.jsonl: the raw secret string appears as a plain "type":"user" message, immediately followed by a genuine "type":"assistant" message with non-zero usage.input_tokens/output_tokens — proving the model actually processed it. There is no hook-related entry anywhere near it.

This reproduced consistently across at least 3 separate fresh sessions (confirmed via distinct session transcript files), including after:

  • adding "shell": "bash" to the hook definition,
  • fully exiting and restarting the claude process each time,
  • confirming no .claude/settings.local.json override exists,
  • confirming the hook appears correctly in /hooks.

Additional notes

  • A separate PreToolUse hook (matcher: Bash), defined the same way in the same settings.json, did fire correctly and block a matching Bash tool call — but only when observed from a different client context (Claude Code running inside the Claude desktop app's "Code" tab), not yet independently confirmed to auto-fire from this same Git Bash CLI session.
  • permissions.deny rules in the same settings.json (blocking Read/Bash cat on .env, ~/.aws/**, etc.) do work reliably and show a clear, non-silent denial message — so the settings file is being loaded and at least partially honored; it specifically appears to be UserPromptSubmit hook invocation that silently doesn't happen.
  • /doctor separately flagged "hook execution error" telemetry for a PreToolUse Bash-hook run in this same project, suggesting hook execution may be broadly unreliable in this environment, not specific to UserPromptSubmit.

Impact

This defeats any credential-leak-prevention hook design relying on UserPromptSubmit to intercept typed prompts before they reach the model — the core guarantee the hook is documented to provide.

Possible regression of a previously-closed issue

This looks like a regression of #17277 ("UserPromptSubmit hook not triggering consistently"), which described the exact same symptom — the hook failing specifically on prompts submitted early in a session — and hypothesized a race condition where hook registration happens asynchronously after session init, so the first prompt(s) arrive before the handler is registered. That issue was closed in January 2026 after the reporter said it "appears to work more consistently with 2.1.3." This report reproduces the identical symptom on v2.1.261, consistently on the first prompt of freshly-restarted sessions, suggesting the underlying race condition has resurfaced. Related closed reports with similar titles: #7873, #31114.

View original on GitHub ↗

This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗