Message delivered to an idle session cold-starts a "ghost turn" (no assistant output), and the queued message is silently lost on app restart — no transcript record remains
Environment
- Claude Code Desktop (Windows 11 Home 26200)
- App versions: 2.1.220 and 2.1.229 — reproduces on both (updating did not fix it)
- Models: multiple tiers in use across sessions (Opus/Sonnet class); permission mode:
autofor all sessions - Scale: ~15–30 concurrent local sessions in one workspace, which message each other via the session-messaging tool
- Related existing issues: #83705 (background-start thread hang), #78338 (queued messages dropped + missing completion notification)
Summary
When session A sends a message to session B while B is idle/asleep, delivery succeeds, but B's
turn never produces output. The turn stays open indefinitely (observed 3.5+ minutes to tens of
minutes) with zero assistant tokens. On app restart, the queued message is dropped without ever
being processed, and — critically — no record of it remains in B's transcript file, so neither
the sender nor the receiver can tell afterwards that a message was lost.
Steps to reproduce
- Open two sessions, A and B, in the desktop app.
- Leave B untouched until it goes idle (our logs show a warm-lifecycle idle timeout of ~900s).
- From A, send a cross-session message to B.
- Observe B.
Expected
B wakes, runs a turn, and processes the message (or surfaces an explicit error if it cannot start).
Actual
- The app logs a cold start:
sendMessage on uninitialized session local_…; cold-starting via startSession
(main.log, 2026-08-14 23:50:17 JST).
- B shows an open turn that generates nothing. No assistant output, no error, no timeout message.
- If the app is restarted while such a turn is pending, the message is never processed and
disappears — see "Silent data loss" below.
- Sessions that are warm (a human typed in them recently, or they just started) process
messages normally. This was consistent all day, in both directions.
Silent data loss (the most damaging part)
After a restart we scanned every transcript (~/.claude/projects/**/*.jsonl, 32 project
directories) for the incident window: zero of that night's incoming messages are present.
Transcript entries appear to be written when a turn executes, so a turn that never runs leaves
no trace at all. The messages are visible only through the app's own event store while the session
is alive.
Consequence: both sides believe the exchange happened. The sender sees a successful send; the
receiver, later, sees nothing in its history and truthfully reports "I received nothing." There is
no post-hoc way to enumerate what was lost.
Workaround attempts (and why they are not sufficient)
- Human nudge ("please handle the messages you received") does make the session run a turn, but
it does not reliably process the queued message: in our test the session ran and reported on an
unrelated topic, ignoring the explicit test instruction that was pending.
- Keeping sessions warm avoids the path entirely, but requires constant activity, which is exactly
what we had turned off for cost reasons — i.e. the failure appears precisely when you economise.
What we ruled out (measured, not assumed)
- Permission mode — all sessions are
auto; verified. - App auto-update — symptom predates the update and survives 2.1.220 → 2.1.229.
- Our own SessionStart hooks — during a ghost turn, no hook
nodeprocesses exist (process list
checked); only MCP server processes are present.
- A specific broken session /
/clear— a freshly cleared session shows the same behaviour. - Our MCP server being slow to start — we time-stamped it: our only local MCP server
(@modelcontextprotocol/server-filesystem) starts in 0.16–2.7 s. A multi-minute hang cannot be
explained by it. (It does log Server transport closed unexpectedly occasionally, but startup is fast.)
- An Anthropic-side status incident — the behaviour continued after status showed resolved.
Impact
For a multi-session workflow this is worse than a crash: it is undetectable by either party, it
loses instructions, and it appears only in the low-activity regime users enter to reduce cost.
Note also that the newer built-in cross-session messaging (@-mentions of sessions) is not available
on native Windows (the @ picker offers only files/apps here), so the MCP-based session-messaging
path this report describes is the only channel available on this platform — and it is broken.
What would fix it for us (in priority order)
- Never fail silently: if the cold start cannot complete, end the turn with a visible error.
- Persist queued messages across restarts, or refuse the send with an error the sender can see.
- Write the transcript entry at receipt time, not at turn-execution time, so that a lost message
is at least auditable afterwards.
- Optional: expose the delivered-but-unprocessed state through the API so tooling can alert on it.
This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗