SessionStart hook additionalContext silently dropped when source=fork (rewind); startup/compact inject normally

Status Open
Reported on v2.1.260
Maintainer reply None cached
Activity 1 comment · opened Sep 10, 2026

Preflight

Searched existing issues. The closest are #69848 (Windows-wide additionalContext injection failure, closed as completed) and #88086 (VS Code extension + plugin hooks, macOS). Both describe broader surfaces. This report is narrower: on a current version, in one install, startup and compact inject correctly and only fork drops the payload.

What's Wrong?

A SessionStart hook that returns hookSpecificOutput.additionalContext runs and produces output when the session is created by a rewind (source=fork), but the model never receives the text.

The same hook, same machine, same project, same settings file injects correctly on source=startup and source=compact — its output is visible in those sessions' context.

Nothing reports a failure: the hook exits 0 and emits valid JSON. Without a side effect inside the hook itself, "the hook didn't run" and "the hook ran and its output was discarded" are indistinguishable.

What Should Happen?

Either the text reaches the model on fork as it does on startup/compact, or the documented contract records the exception.

Today the docs say the opposite. From Add context for Claude:

The additionalContext field passes a string from your hook into Claude's context window. Claude Code wraps the string in a system reminder and inserts it into the conversation at the point where the hook fired.
Where the reminder appears depends on the event: * SessionStart and SubagentStart: at the start of the conversation, before the first prompt

From the SessionStart section, forked sessions are explicitly expected to run SessionStart hooks:

| fork | A new session forked from an existing one: --fork-session with --resume or --continue, the /fork background copy, or /branch |
When source is "resume" or "fork" and the transcript contains at least one response from Claude, SessionStart hooks also receive the four fields below.

(Edited: an earlier version of this section quoted a sentence about additionalContext that is not in the docs — it came from an automated summary of the page, not the page itself. The quotes above are verbatim from the page's raw markdown.)

And from Matcher patterns, fork is a documented SessionStart source:

| SessionStart | how the session started | startup, resume, clear, compact, fork |

There is no stated condition under which SessionStart additionalContext is not applied.

If this is intentional, the rationale I can imagine is that a forked session inherits the parent's context, which already contains the original injection, so re-injecting would duplicate it. That is reasonable — but it should be documented, because the inherited copy is a snapshot from the fork point. My hook surfaces state that changes during a session: a parity check's current failures, findings written by a SessionEnd hook, a to-do queue. After a rewind the model silently keeps reading the stale version, with no signal that it is stale.

Error Messages/Logs

No error — the hook succeeds. The evidence is the app's own rewind log line paired with the hook's self-trace, one second apart.

App log (%LOCALAPPDATA%\Claude\Logs\main.log):

2026-09-11 03:36:24 [info] [Rewind] resumeSessionAt=80b720a2-3ec9-4011-a5a2-c6e516465920 + forkSession for session local_8021f6c5-e5fc-405e-8bc0-802d8729512b

The hook's own trace file, written by the hook as its last act before printing its JSON:

2026-09-11 03:36:25 session_orient SessionStart ran:source=fork 注入 2430 字元

(The trace is in Chinese: "注入 2430 字元" = "emitted 2430 characters of additionalContext".)

The session created by that rewind contained none of those 2430 characters. Neighbouring lines in the same trace file show SessionEnd hooks firing normally around the same minutes, so hooks as a whole were live.

Steps to Reproduce

  1. Register a SessionStart hook in .claude/settings.json that (a) appends a line to a log file recording the source field from its stdin payload, and (b) prints {"hookSpecificOutput":{"hookEventName":"SessionStart","additionalContext":"MARKER_ABC123"}}.
  2. Start a session in that project. Ask: "Is the string MARKER_ABC123 in your context?" → yes.
  3. Change the marker to MARKER_XYZ789, then rewind the conversation to a message before step 2 (Claude Code desktop app; the app logs [Rewind] resumeSessionAt=... + forkSession).
  4. In the resulting session, ask: "Is the string MARKER_XYZ789 in your context?" → no.
  5. Open the hook's log file: it has a new line with source=fork, proving the hook ran in step 4 and produced a payload that never arrived.

Step 5 is the part that matters. Without an in-hook side effect there is no way to tell this apart from the hook simply not firing.

Claude Model

Opus 5 (claude-opus-5)

Is this a regression?

Unknown. I only added the hook self-trace on 2026-09-11 and hit this within minutes, so I have no observation of the earlier behaviour.

Claude Code Version

2.1.260 — the desktop app's bundled binary (main.log: Using Claude Code binary at: …\claude-code\2.1.260\claude.exe).

(Edited: this originally said 2.1.263, which is a separately installed CLI on my PATH, not the binary that ran the session.)

Platform

Anthropic API (Claude subscription)

Operating System

Windows 10 (10.0.19045)

Terminal/Shell

Claude Code desktop app, Code tab — not the terminal CLI and not the VS Code extension. Hook commands run through Git Bash.

Additional Information

Rewinds are not rare in my usage: 5 [Rewind] ... forkSession entries in the current main.log. Each one silently drops that session's orientation payload.

If the behaviour is kept as-is, a one-line note under SessionStart in the hooks docs — "additionalContext is not applied for source: fork; the forked session inherits the parent's context" — would be enough to save the next person the investigation.

View original on GitHub ↗

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