Desktop app and remote-control sessions never render hook `systemMessage` (PreToolUse/PostToolUse)

Open 💬 1 comment Opened Jul 14, 2026 by johnsmyth

Summary

A hook's systemMessage — the user-facing output channel — is never rendered in the Claude Desktop app, nor in the Desktop/web/mobile mirror of a remote-controlled terminal session. It renders correctly in the terminal.

The hook demonstrably runs on all surfaces: hookSpecificOutput.additionalContext reaches the model and hookSpecificOutput.permissionDecision is honoured. Only the human-facing channel is dropped.

Repro

  1. Install a PreToolUse hook that emits, on stdout:
{
  "systemMessage": "[Example] WARNING — this tool call touched a credential",
  "hookSpecificOutput": {
    "hookEventName": "PreToolUse",
    "permissionDecision": "allow",
    "additionalContext": "This tool call touched a credential."
  }
}
  1. Trigger it in each of:
  • a terminal session
  • a Desktop-native session (started in the Claude Desktop app)
  • a terminal session mirrored via remote control (claude --remote-control)

Expected

The systemMessage text is shown to the user on all three surfaces.

Actual

It is shown only in the terminal. In the Desktop app — both native sessions and the remote-control mirror — nothing is displayed.

The hook did run. In our Desktop-native test the model received additionalContext and spontaneously mentioned it in its reply:

"The guardian hook did fire on it. It came back with Risk: high — printf /tmp/gd-desktop-probe.env (unknown)"

…while the Desktop UI itself rendered no hook output whatsoever. Our daemon independently recorded the warning against that exact session (warn_count incremented), confirming the hook fired and produced user-facing text that was then discarded by the renderer.

Impact

Security and policy hooks that warn the user via systemMessage are silently invisible on Desktop.

This fails in the worst possible direction: the agent still receives the warning and enforcement still applies, so nothing looks broken from inside the session — the user simply never learns their tool call was flagged. There is no error, no fallback, and no way for the hook author to detect the degradation. A hook whose entire user-visible surface is systemMessage (a reasonable reading of the docs, which describe it as the channel for messages to the user) goes from fully functional in the terminal to completely silent on Desktop.

In our case a credential-exposure warning fired, was recorded, and the human saw nothing.

Related

  • #74299 (open) — same class of failure, but covers the top-level decision / reason fields. Distinct from this: decision/reason are not emitted on PreToolUse, so that issue does not cover the systemMessage path.
  • #35707 (closed) — same failure for systemMessage from Stop hooks in remote mode. Suggests this was fixed for one hook type but not for PreToolUse / PostToolUse.
  • #42336 (closed) — PostToolUse hooks not triggering in Desktop.

Environment

  • Claude Code v2.1.209
  • macOS 15.5 (darwin 25.5.0)
  • Claude Desktop app

View original on GitHub ↗

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