claude-code - 💡(How to fix) Fix UserPromptSubmit hook: decision: "block" does not erase typed prompt from model context [1 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
anthropics/claude-code#55143Fetched 2026-05-01 05:45:09
View on GitHub
Comments
0
Participants
1
Timeline
5
Reactions
0
Author
Participants
Timeline (top)
labeled ×5

When a UserPromptSubmit hook returns hookSpecificOutput.decision: "block", the docs state the prompt is "erased from context" and "removed from the transcript." Empirically this does not appear to be the case — the literal text the user typed reaches the model in conversation context, and the model can quote it back in the same or subsequent turns.

This breaks the intended design of any hook that uses block to prevent sensitive text (PII, secrets, etc.) from reaching the model.

Root Cause

When a UserPromptSubmit hook returns hookSpecificOutput.decision: "block", the docs state the prompt is "erased from context" and "removed from the transcript." Empirically this does not appear to be the case — the literal text the user typed reaches the model in conversation context, and the model can quote it back in the same or subsequent turns.

This breaks the intended design of any hook that uses block to prevent sensitive text (PII, secrets, etc.) from reaching the model.

Code Example

{
  "hookSpecificOutput": {
    "hookEventName": "UserPromptSubmit",
    "decision": "block",
    "reason": "[foo] registered SECRET_PLACEHOLDER",
    "additionalContext": "[foo] registered SECRET_PLACEHOLDER"
  }
}

---

foo: bar SOME_DISTINCTIVE_STRING
RAW_BUFFERClick to expand / collapse

Summary

When a UserPromptSubmit hook returns hookSpecificOutput.decision: "block", the docs state the prompt is "erased from context" and "removed from the transcript." Empirically this does not appear to be the case — the literal text the user typed reaches the model in conversation context, and the model can quote it back in the same or subsequent turns.

This breaks the intended design of any hook that uses block to prevent sensitive text (PII, secrets, etc.) from reaching the model.

Repro

  1. Register a UserPromptSubmit hook (e.g. a small shell script invoking a Python module) that, for any prompt matching ^foo: bar , returns a sanitized receipt that does NOT contain the user-supplied argument:
{
  "hookSpecificOutput": {
    "hookEventName": "UserPromptSubmit",
    "decision": "block",
    "reason": "[foo] registered SECRET_PLACEHOLDER",
    "additionalContext": "[foo] registered SECRET_PLACEHOLDER"
  }
}
  1. Verify the hook output directly — the user-supplied argument appears NOWHERE in the JSON the hook emits.

  2. In an interactive Claude Code session with the hook installed, type:

foo: bar SOME_DISTINCTIVE_STRING
  1. Observe the model's response. The model can reference SOME_DISTINCTIVE_STRING in its reply — for example by paraphrasing it back, or by mapping it to the placeholder ("SOME_DISTINCTIVE_STRINGSECRET_PLACEHOLDER").

The string is not in the hook output. The only place it existed is the user's typed prompt, which decision: "block" is supposed to have erased from context.

Expected behavior

Per the hooks docs:

When decision: "block" is set, the prompt is erased from context and removed from the transcript.

The model should never see the typed prompt — only the reason string should be surfaced (to the user, not the model).

Actual behavior

The model sees the typed prompt verbatim and can quote it back on the same turn. (Subsequent-turn behavior not yet rigorously confirmed but appears similar.)

Notes

  • The hook output sets both reason and additionalContext. Per the docs, additionalContext is ignored when decision: "block" is set, so this should not matter — the leak appears regardless.
  • Anecdotally, when the cleartext is placed in the body (lines after the trigger line) rather than as args on the trigger line, the leak is harder to reproduce. So this may be specific to the trigger / first line of a multi-line prompt, or specific to single-line prompts. Happy to dig deeper if a maintainer wants more data.

Environment

  • Platform: Windows 11 Enterprise
  • Shell: Git Bash + PowerShell
  • Claude Code version: 2.1.123
  • Model: claude-opus-4-7

Why it matters

decision: "block" is the only documented mechanism for a UserPromptSubmit hook to prevent text from reaching the model. There is no documented replacePrompt / rewrite mechanism. If block doesn't actually block, hooks designed to redact sensitive user input before it reaches the model have a silent leak.

extent analysis

TL;DR

The decision: "block" in UserPromptSubmit hook does not effectively prevent sensitive text from reaching the model, requiring a reevaluation of the hook's implementation or the documentation.

Guidance

  • Review the documentation for UserPromptSubmit hook to ensure correct usage of decision: "block" and its implications on prompt context and transcript.
  • Verify that the hook output does not contain the sensitive text, but also check the model's response to ensure it does not reference the blocked prompt.
  • Consider testing the hook with different prompt formats, such as multi-line prompts or prompts with the sensitive text in the body, to see if the behavior changes.
  • If the issue persists, it may be necessary to revisit the design of the hook and explore alternative methods for redacting sensitive user input.

Notes

The behavior described may be specific to certain prompt formats or versions of Claude Code, so further testing and investigation are needed to fully understand the issue.

Recommendation

Apply workaround: Until the root cause is identified and fixed, consider using alternative methods to redact sensitive user input, such as preprocessing the prompt before passing it to the model, if possible.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

FAQ

Expected behavior

Per the hooks docs:

When decision: "block" is set, the prompt is erased from context and removed from the transcript.

The model should never see the typed prompt — only the reason string should be surfaced (to the user, not the model).

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

claude-code - 💡(How to fix) Fix UserPromptSubmit hook: decision: "block" does not erase typed prompt from model context [1 participants]