SessionStart hook systemMessage not surfaced in VSCode extension side panel (works in terminal CLI)

Status Open
Reported on v2.1.218
Maintainer reply None cached
Activity 3 comments · opened Jul 24, 2026

Bug Description

A SessionStart hook that outputs a systemMessage (standard local hooks.json, no remote-control) renders the message in the terminal CLI but not in the VSCode native extension side panel. The message never appears in the panel — not inline, not as a dialog, not late.

The hook's additionalContext output is ingested correctly on both surfaces (the model receives it); only the user-visible systemMessage is dropped in the extension panel.

This is distinct from #52775 (which is specifically about remoteControlAtStartup rendering systemMessage as a late dismissable dialog in the terminal) and #33612 (hooks in remote-settings.json not firing for non-terminal clients). Here the hook fires, additionalContext lands, remote-control is off, and it's plain local settings — only the panel rendering of systemMessage is missing.

Steps to Reproduce

  1. Add a SessionStart hook to .claude/settings.json (or a plugin's hooks.json) that emits both fields:

``json
{
"hooks": {
"SessionStart": [{
"matcher": "startup|resume|clear",
"hooks": [{
"type": "command",
"command": "echo '{\"hookSpecificOutput\":{\"hookEventName\":\"SessionStart\",\"additionalContext\":\"hello-context\"},\"systemMessage\":\"hello-visible-message\"}'"
}]
}]
}
}
``

  1. Start a session in the terminal CLI → hello-visible-message is shown (as SessionStart:startup says: …).
  2. Start a session in the VSCode native extension (open the Claude Code side panel) → hello-visible-message is not shown anywhere in the panel.

In both surfaces the model does receive additionalContext ("hello-context"), confirming the hook itself fires correctly on both.

Expected Behavior

The systemMessage from a SessionStart hook should be surfaced to the user in the VSCode extension side panel — matching the terminal CLI, which echoes it at startup. SessionStart systemMessage is commonly used to show session posture/context (loaded config, active skills, repo state) so the user can see it at a glance; that affordance is silently unavailable in the extension.

Environment

  • Claude Code VSCode extension: anthropic.claude-code@2.1.218
  • Platform: Linux (WSL2, VSCode Remote / .vscode-server)
  • Hook type: SessionStart (matcher startup|resume|clear), local hooks.json
  • Hook output: {"hookSpecificOutput": {"additionalContext": "..."}, "systemMessage": "..."}
  • Remote-control: not active

Impact

Plugins/skills that rely on SessionStart systemMessage for a visible posture greeting or environment summary appear to "do nothing" in the VSCode extension, even though the hook is working and the model-facing context is delivered. Authors have no user-visible SessionStart surface in the panel.

View original on GitHub ↗

3 Comments

mattlavine · 1 month ago

Confirming this on a third client, and adding one finding that closes off the documented workaround.

1. Same bug in the macOS desktop app

Not just the VSCode extension panel. Claude Code 2.1.233, macOS 26.5, arm64, plain local ~/.claude/settings.json, no remote-control.

Identical SessionStart hook, same commit, same machine, run on both surfaces:

| output | terminal CLI | desktop app |
| --- | :---: | :---: |
| systemMessage | renders — SessionStart:startup says: … | not shown |
| hookSpecificOutput.additionalContext | ingested | ingested (model receives it) |

Verified across two independent fresh desktop sessions. Matches your report exactly: the hook fires, additionalContext lands on both, only the user-visible systemMessage is dropped on the non-terminal client.

2. exit-2 stderr is dropped too — so there is no fallback

This is the part I think is new. The hooks documentation offers exit 2 as the other user-visible path for this event:

For SessionStart, Setup, and SubagentStart, the exit code 2 stderr renders in the transcript as a <hook name> hook error notice … Claude doesn't see it, and the session proceeds.

I tested it with a throwaway hook that writes an unmistakable marker to stderr and exits 2:

#!/usr/bin/env python3
import sys
sys.stderr.write("EXIT2-PROBE-9F3A — marker\n")
sys.exit(2)

Wired as a second SessionStart hook alongside the first:

| output | terminal CLI | desktop app |
| --- | :---: | :---: |
| exit-2 stderr | renders — SessionStart:startup hook error … EXIT2-PROBE-9F3A | not shown |

The CLI is a clean positive control here: it rendered both channels in the same session, so the probe fired and the wiring was correct. Two desktop sessions showed neither marker.

So on non-terminal clients there is currently no way for a SessionStart hook to say anything to the user. systemMessage is dropped, and the documented exit-2 fallback is dropped as well. The only thing that survives is additionalContext, which reaches the model — so the user learns about it only if the model happens to mention it.

Why this matters beyond cosmetics

SessionStart is where "state you should know before you start" naturally lives. In my setup this silently disabled four such hooks for a desktop-primary user, including one whose entire job is to announce that the guard kill-switch is active — i.e. a warning that all safety hooks are disabled had 225 recorded firings and was never once visible to the human.

A daily backup job in the same setup failed for four days while a SessionStart hook reported it perfectly — to the model.

Repro (desktop app)

  1. ~/.claude/settings.json:

``json
{"hooks":{"SessionStart":[{"hooks":[{"type":"command",
"command":"echo '{\"hookSpecificOutput\":{\"hookEventName\":\"SessionStart\",\"additionalContext\":\"ctx-marker\"},\"systemMessage\":\"VISIBLE-MARKER\"}'"}]}]}}
``

  1. Start a session in the terminal CLI → VISIBLE-MARKER appears.
  2. Start a session in the desktop app → it does not.
  3. Replace the command with the exit-2 script above → same split.

Happy to test a candidate fix on the desktop app if that helps.

adriaan-wessels · 1 month ago

Confirming on a fourth client configuration — the Windows desktop app — and adding a same-session control that narrows the bug to the SessionStart event specifically, rather than notice rendering in general.

Windows desktop app, Claude Code 2.1.251

  • Platform: Windows 11 Home 10.0.26200, native desktop app, plain local project hooks, no remote-control.
  • A SessionStart hook emits the standard two-channel envelope on stdout:

``json
{"systemMessage": "<user-facing text>", "hookSpecificOutput": {"hookEventName": "SessionStart", "additionalContext": "<model-facing text>"}}
``

  • additionalContext is ingested (the model demonstrably received and acted on it in the same session), proving the envelope parsed and the hook fired.
  • systemMessage never renders. Verified by scrolling to the very top of the session transcript: nothing appears above the first user message — not inline, not as a dialog, not late. Matches the VSCode-panel report and the macOS desktop confirmation above exactly.

Control: Stop hook systemMessage renders fine on the same surface, same session

In the same desktop-app session, a Stop hook returning a root-level systemMessage rendered normally as a visible "Claude Code notice" in the transcript. So the desktop app's notice rendering works in general — the drop is specific to the SessionStart event (consistent with the SubagentStart/Setup family sharing a startup-time path, if that's the mechanism).

Docs note: the hooks reference documents systemMessage for both the SessionStart and Stop sections identically as "Displayed in the transcript as a system message," with no per-surface caveat — so on non-terminal clients the SessionStart half of that is currently not true.

Practical impact matches the OP: the only reliable user-visible delivery for SessionStart information on these surfaces is asking the model to relay additionalContext — a workaround, not a channel.

guruchill · 1 month ago

Independent repro (5th, per the thread) — same symptom, different platform combo:

  • Client: Claude Code VSCode native extension host
  • Platform: Windows 11 Pro (10.0.26200)
  • Setup: local project-level .claude/settings.json, SessionStart hook (no matcher), local Node command hook emitting {"systemMessage": "...", "hookSpecificOutput": {"hookEventName": "SessionStart", "additionalContext": "..."}}
  • Confirmed: additionalContext reliably reaches the model's context every session (verified via the model surfacing it back). systemMessage never appeared anywhere in the UI across multiple fresh session starts — no inline text, no dialog, no notification, immediate or delayed.
  • Ruled out: folder trust (hasTrustDialogAccepted: true, confirmed), hook not firing (disproved by additionalContext landing), malformed JSON (hook output validated manually via both PowerShell and Git Bash before relying on it in-session).

Workaround in use: drop systemMessage entirely and instruct the model via additionalContext to proactively relay the status as its own chat text at session start, since that's the only channel that's actually reaching the user on this client today.