MessageDisplay hook returns valid displayContent but Claude Code renders the original text (terminal CLI, 2.1.221)
Preflight
- [x] I have searched open and closed issues for duplicates
- [x] This is a single bug report, not multiple issues
- [x] I am on the latest version (2.1.221)
What's Wrong?
A MessageDisplay hook is invoked correctly with a well-formed payload and returns valid hookSpecificOutput.displayContent, but Claude Code renders the original message text unchanged. The returned displayContent is silently discarded — nothing the hook returns ever reaches the screen.
This is not a registration failure: the hook demonstrably fires. I instrumented the hook to log its stdin, exit code, and stdout, and confirmed over 22 consecutive invocations that it receives the correct payload and emits valid output every time.
To rule out any subtlety about partial replacement, I made the hook prefix every chunk with an unmissable marker (not just index == 0). It still rendered nothing — a hook can replace the entire visible message text and the screen shows the original.
What Should Happen?
The string returned in hookSpecificOutput.displayContent should replace the delta on screen, per the event's own schema description: "Text displayed in place of the delta."
Steps to Reproduce
- Install
jqand be on Claude Code 2.1.221. - Add this to
~/.claude/settings.json(no plugin required):
{
"hooks": {
"MessageDisplay": [
{
"hooks": [
{
"type": "command",
"command": "jq '{hookSpecificOutput:{hookEventName:\"MessageDisplay\",displayContent:(\">>>MARKER<<< \" + .delta)}}'"
}
]
}
]
}
}
- Fully restart Claude Code.
- Send any prompt.
Expected: every assistant message on screen is prefixed with >>>MARKER<<<.
Actual: no marker appears anywhere; messages render exactly as if no hook existed.
Error Output / Evidence
The hook logging its own stdin and stdout, from a real session. Payload is correct, jq exits 0, and valid displayContent is returned:
=== INVOKED at 17:52:40 ===
jq: OK (/c/Users/salvi/AppData/Local/.../jqlang.jq_.../jq)
STDIN: {"session_id":"6af6871a-...","hook_event_name":"MessageDisplay",
"turn_id":"9fdab17d-...","message_id":"55632857-...",
"index":0,"final":false,"delta":"Instrumentation is live..."}
jq rc=0
OUT: {
"hookSpecificOutput": {
"hookEventName": "MessageDisplay",
"displayContent": "[17:52:40] Instrumentation is live..."
}
}
None of that displayContent was rendered.
Additional Context
Things I verified, so they can be excluded:
- Not plugin-specific. Reproduced identically with the hook declared in a plugin's
hooks/hooks.jsonand declared directly in~/.claude/settings.json. Both fail the same way, including after a full restart. - Not a registration failure. The hook is invoked every time; only its output is ignored.
- Not
jqor Git Bash.jqresolves from inside the hook process itself (logged), and the same script runs correctly when piped a sample payload by hand. - Not a payload mismatch. The hook receives
indexanddeltaexactly as documented. - Other hook events work fine in the same setup — a
UserPromptSubmithook from the same source injectsadditionalContextthat is honored on every prompt. OnlyMessageDisplayrendering is dead.
Possibly related, but I do not believe any is a duplicate:
- #69380 (closed) and #70381 — plugin
hooks.jsonfailing to registerMessageDisplay. Different failure mode: here the hook registers and fires, and the asymmetry survives even when the hook is moved tosettings.json, which was the workaround reported in #69380. - #66555, #76736, #78266 — hook output not rendering, but all in the Desktop app / VS Code extension. This report is the plain terminal CLI (
CLAUDE_CODE_ENTRYPOINT=cli, Windows Terminal, no VS Code attached), so the surface differs. It may still share a root cause in the render path.
Discovered while debugging the message-timestamps plugin, but reduced above to a minimal, plugin-free repro.
Claude Code Version
2.1.221
Is this a regression?
Not sure — this is my first use of the MessageDisplay event, so I have no known-good earlier version.
Platform
Anthropic API
Operating System
Windows 11 Home (10.0.26200)
Terminal/Shell
Windows Terminal; hooks execute via Git Bash
This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗