[BUG] PostToolUse hook exit-2/stderr warning not delivered to model in VSCode extension
Preflight Checklist
- [x] I have searched existing issues and this hasn't been reported with this scope
- [x] This is a single bug report
- [x] I am using the latest version of Claude Code (v2.1.234)
What's Wrong?
Follow-up to #55889 (closed for inactivity, not resolved — locked, so filing new per its own closing instructions). That issue documented additionalContext/systemMessage/plain-stdout-on-exit-0 all being dropped for Bash-matched PostToolUse hooks. It did not test exit 2 + stderr, which several community docs/threads recommend as the reliable fallback (since exit-0 stdout is documented as not surfaced to the model, only exit-2 stderr is). In our testing, exit 2 + stderr is also not delivered to the model in the VSCode extension.
We confirmed with a two-stage diagnostic, isolating "does the harness invoke the hook" from "does the hook's message reach the model":
- Added an unconditional debug line at the top of our PostToolUse hook script that appends a timestamp to
/tmp/check-commit-debug.log, before any conditional logic. - Ran multiple real
git commitcommands from within a live Claude Code session (VSCode extension, not terminal CLI). - Confirmed via
cat /tmp/check-commit-debug.logthat the hook is invoked on every commit — the harness-to-script wiring works correctly. - The same script, further down, detects a specific commit condition (a new file added under a given path) and on match does:
``bash``
echo "<warning text>" >&2
exit 2
- On a commit that met the match condition, the debug log confirmed the script ran and reached that branch. But the Claude model in the session never referenced, mentioned, or acted on the warning text — no awareness of it at all, across multiple repeated attempts, including after a full VSCode window restart.
This means for Bash-matched PostToolUse hooks in the VSCode extension, none of the documented-or-recommended delivery channels work: additionalContext (per #55889), systemMessage (per #55889), plain stdout on exit 0 (per #55889), and now exit 2 + stderr (this issue).
What Should Happen?
Per the hooks reference, PostToolUse hook output should appear next to the tool result so the model can see and act on it. Exit 2 + stderr specifically is documented elsewhere as the channel to use for a non-blocking-but-visible warning. Neither happens.
Reproduction
- In a repo, add
.claude/settings.json:
``json``
{
"hooks": {
"PostToolUse": [
{
"matcher": "Bash",
"hooks": [
{ "type": "command", "command": "./check-commit.sh", "if": "Bash(git commit*)", "timeout": 10 }
]
}
]
}
}
check-commit.sh:
``bash``
#!/usr/bin/env bash
set -euo pipefail
echo "invoked at $(date -Iseconds)" >> /tmp/check-commit-debug.log
echo "XYZZY_TEST_MARKER: if you see this, say MARKER_OK" >&2
exit 2
- In a live Claude Code session running inside the VSCode extension (not terminal CLI), have Claude run
git commit --allow-empty -m "test". - Observe:
/tmp/check-commit-debug.loggets a new line (hook fires), but the model's next message shows no awareness ofXYZZY_TEST_MARKER— it never saysMARKER_OK. - Tried across multiple separate commits and after a full VSCode window/extension restart — same result every time.
Environment
- Claude Code Version: 2.1.234
- VSCode: 1.133.0
- Extension: anthropic.claude-code@2.1.234
- OS: Linux (Fedora), connected via Remote-SSH
- Related: #55889 (PreToolUse/PostToolUse additionalContext/systemMessage/stdout dropped, closed for inactivity — this issue extends that finding to exit-2/stderr and confirms in VSCode extension specifically)
3 Comments
Update: retested the exact same hook script and repro via
claude -pon the terminal CLI (same repo, same machine, same Remote-SSH session) and the exit-2/stderr warning was delivered correctly — the model received and quoted it verbatim, prefixed with[dev-workflow/check-commit.sh]:.This isolates the bug specifically to the VSCode extension (anthropic.claude-code@2.1.234) — not a general exit-2/stderr delivery bug, not SSH-remote-specific, not the hook script. Terminal CLI (same version, 2.1.234) delivers PostToolUse exit-2/stderr correctly. Updating the title/scope would help future searchers distinguish this from the broader-scope reports in #55889 (which found additionalContext broken on CLI too, for a different channel).
Closing this — turned out to be a misdiagnosis on our end, not a Claude Code bug. Documenting what we found in case it helps anyone else who lands here from a search.
Root cause: we had the delivery channel wrong for
PostToolUse. Per the hooks reference "Decision Control" section,PostToolUseis in the top-level-decision group, not the exit-code-2 group — exit 2 + stderr is the contract for blockingPreToolUsehooks (where it makes sense: the tool hasn't run yet, so a warning can actually prevent the action). ForPostToolUse, the tool has already completed by the time the hook runs, so there's nothing to block — the documented contract there is a JSON object on stdout with exit 0:("block" here doesn't undo the already-completed action — it attaches
reasonas feedback on the tool result for the model to see.)We switched our hook (
echo "..." >&2; exit 2→ the stdout-JSON form above) and it now works reliably — confirmed live in a real session in both the VSCode extension and the terminal CLI, using the same two-stage diagnostic (debug-log instrumentation to confirm invocation, separate from confirming model-side delivery) that produced this bug report in the first place.So: exit-2/stderr genuinely doesn't reach the model for
PostToolUse— that reproduction step in our original report was accurate — but that turns out to be correct behavior per the docs, not a bug, sincePostToolUsewas never supposed to use that channel. Apologies for the noise; leaving this comment rather than deleting the issue in case the "exit-2/stderr silently doesn't work for PostToolUse, even though several community threads recommend it as a fallback" finding is useful to someone else who, like us, followed that advice without re-checking which hook-event group it applies to.Reference (our fix + writeup): https://github.com/sborenst/control_room/issues/3
This issue has been automatically locked since it was closed and has not had any activity for 7 days. If you're experiencing a similar issue, please file a new issue and reference this one if it's relevant.