[BUG] SessionStart hook fires for sessions that never materialize (no transcript file is ever created) - side effects get attributed to a real, named session
Preflight Checklist
- [x] I have searched existing issues and this hasn't been reported yet
- [x] This is a single bug report (please file separate reports for different bugs)
- [x] I am using the latest version of Claude Code
What's Wrong?
Sessions are started with source=startup at a rate of tens per hour that never create a transcript file. Each one fires the SessionStart hook.
If the hook has any side effect keyed to session identity, that side effect is performed on behalf of a session that never actually ran.
Evidence
Our hook logs every invocation. For 2026-08-22:
83 SessionStart events logged
15 had a transcript file that exists on disk
68 did not (82%)
The 15 real ones map to sessions we can account for. The 68 do not correspond to anything a user or a scheduled task started.
Concrete harm
Our SessionStart hook delivers inter-session messages into context and marks them read.
The phantom sessions marked messages as read on behalf of sessions that never saw them. From the outside the message looked delivered; in reality nobody had read it.
This is worse than non-delivery, because our staleness alarm keys off unread. A message that is never delivered eventually rings. A message marked read by a phantom never rings at all - it is silently consumed.
What Should Happen?
Either of:
- Do not fire
SessionStartfor sessions that will not materialize, or - give the hook a reliable signal at invocation time distinguishing "this is a real session" from "this is a probe, or a session that will never produce a transcript".
Even documenting the guarantee would help. At present transcript_path is passed to the hook, but nothing states that it may point at a file which will never exist, so a hook author reasonably assumes the session is real.
Error Messages/Logs
No error is produced - the hook runs normally and succeeds. The only trace is our own log.
Counts for 2026-08-22, taken from our hook's own invocation log cross-referenced against the filesystem:
83 SessionStart events (source=startup)
15 transcript_path points at a file that exists
68 transcript_path points at a file that never appears
Workaround now in place in our hook:
if not transcript_path or not os.path.exists(transcript_path):
return 0
We verified against the same day's data that this does not reject real sessions: all 15 sessions whose transcript existed were genuine.
Steps to Reproduce
- Configure a
SessionStarthook that logssession_id,source, andtranscript_pathon every invocation. - Leave the desktop app running normally for a day with several sessions open.
- Compare the logged
transcript_pathvalues against the filesystem. - Observe that a large majority of
source=startupinvocations reference a transcript file that is never created.
In our case: 83 invocations in one day, 68 of them (82%) for sessions with no transcript.
Claude Model
None
Is this a regression?
Yes, this worked in a previous version
Last Working Version
_No response_
Claude Code Version
2.1.237 (Claude Code)
Platform
Anthropic API
Operating System
macOS
Terminal/Shell
Terminal.app (macOS)
Additional Information
Environment
- Claude Code 2.1.237, Windows desktop app (MSIX)
- OS: Windows 11 Home 10.0.26200
- Locale: ja-JP
- About 18 concurrent local sessions on one machine
SessionStartandUserPromptSubmithooks configured
Note on the rate
68 phantom starts in one day on one machine is high enough that any hook doing per-session bookkeeping - logging, counters, notifications, message delivery - will accumulate a large amount of false activity before anyone notices, precisely because each individual event looks legitimate.
Related
Two other reports from the same setup, both about a session losing state without being told: #88732 (/clear silently terminates persistent Monitor tasks) and #88734 (a Monitor surviving as an orphan). They are separate bugs; the common thread is that the session has no way to observe that something it depends on has stopped being true.