Session resume loses conversation messages when SubAgent progress chain overtakes main conversation chain
Summary
When resuming a session (via --resume, VS Code reload, or process restart), conversation messages can silently disappear from both the UI and LLM context. This happens when a SubAgent's progress messages create a parallel parentUuid branch that has a slightly newer timestamp than the main conversation branch.
Steps to Reproduce
- Start a conversation where the main agent dispatches a SubAgent via the
Agenttool - The SubAgent runs and generates progress messages (chaining
parentUuidfrom the assistant message that made theAgenttool_use call) - Meanwhile, the main agent receives the SubAgent result and continues the conversation for several turns
- The SubAgent's buffered progress messages are flushed to the JSONL after the main conversation messages, so the last progress message has a timestamp slightly newer than the last conversation message
- Kill the CLI process (
pkill) or restart VS Code - Resume the session
Expected Behavior
All conversation messages (user + assistant) should be visible in the UI and present in the LLM context after resume.
Actual Behavior
The conversation messages between the SubAgent dispatch and the next independently-rooted message are silently lost — gone from both the UI and the LLM context. Only the progress chain path is preserved.
In my case, 28 messages (including substantive assistant analysis and user responses) disappeared after resume with zero indication.
Root Cause
The parentUuid chain reconstruction during session resume picks the most recent non-sidechain message as the tree tip, then traces backwards. Progress messages are not treated as sidechain, so they participate in tip selection.
When the main agent calls a SubAgent, two branches grow from the same parent node:
L_agent (assistant, Agent tool_use)
├── Branch A: tool_result → conversation continues → ... → L_last_conv (ts=T1)
└── Branch B: progress → progress → ... → L_last_progress (ts=T2)
└── system message → user message → ... (post-resume conversation)
If T2 > T1 (which happens when progress messages are buffered and flushed after conversation messages), the chain reconstruction follows Branch B, and Branch A is completely orphaned.
The post-resume system/user messages chain from the last progress message (Branch B), so once the session is resumed, all subsequent messages are on Branch B. The conversation in Branch A becomes permanently unreachable.
Suggested Fix
Exclude progress messages from tip selection during chain reconstruction. Progress messages don't enter the LLM context by design, so they shouldn't influence which conversation branch is selected.
Something like:
// Current: only excludes sidechain
.filter((P) => !P.isSidechain)
// Fix: also exclude progress
.filter((P) => !P.isSidechain && P.type !== "progress")
Impact
- Silent data loss: No warning or indication that messages were dropped
- Not rare: Triggers whenever SubAgent progress flush is delayed relative to main conversation — common with long-running SubAgent tasks
- Not pkill-specific: Any session reload (VS Code restart,
--resumefrom CLI, window reload) can trigger this if the JSONL tree has this branching structure
Environment
- Claude Code VS Code extension
- macOS
This issue has 9 comments on GitHub. Read the full discussion on GitHub ↗