Repository navigation
Description
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
Activity
github-actions commentedon Mar 12, 2026
Found 3 possible duplicate issues:
This issue will be automatically closed as a duplicate in 3 days.
🤖 Generated with Claude Code
gvelesandro commentedon Mar 13, 2026
@hatawong I'm researching failures where session resume follows the wrong branch of context after subagent work. Your report is one of the clearest public examples I've seen because the subagent progress chain appears to overtake the main conversation and silently orphan 28 messages.
If you're open to it, I'd value one short written postmortem here: https://www.agentsneedcontext.com/agent-failure-postmortem
I'm looking for five specifics: what the main agent and subagent were trying to do, what context disappeared, where that context should have lived, what workaround you used, and what it cost. No pitch, just research.
TeddyRoosevelty commentedon Mar 15, 2026
I've been having what I'm 90% sure is the same bug independently on Windows/Cursor and have some additional findings that might be useful.
Added it here because this issue seemed the MOST relevant compared to the others. Let me know if you'd prefer a different issue.
For me, the "write" path is where the bad data originates
The forked parentUuid chain isn't just a read-path problem — the incorrect links are written to the JSONL at insert time. In
insertMessageChain()(search forsourceToolAssistantUUIDin the CLI source), thelastUuidtracker that determines each message'sparentUuidis updated for every message type, including progress. So when progress messages are appended, the tracker advances past them, and the next real message gets parented to the last progress message instead of the last real message. So the message paths branch, and any messages that occur before the status return or any in that path get completely hidden from context (kept in the file).This means the JSONL on disk has structurally broken links from the moment they're written — it's not just a chain reconstruction issue on reload.
So this doesn't seem limited to SubAgent progress
I'm hitting this with hook progress messages from the built-in extension hooks (PreToolUse/PostToolUse for Edit, Write, Read, MultiEdit). The CLI emits progress messages for hook lifecycle events — and appears to do so even when no user-defined hook matches. This means it can trigger on nearly every tool use in a normal conversation, not just SubAgent dispatches.
Basic Reproduction Steps
[Using: Use Cursor VS Code Addon]
New Session
Say Hi
Ask it to tell me what is in a file (using the read tool)
Say thanks
Reopen the convesation in the conversation
JSONL repair workaround
I wrote a repair script that fixes already-broken files. For each non-progress message whose
parentUuidpoints to a progress message, it rewires the parent to the last non-progress message preceding it in file order. Tested across all the affected conversations above. Happy to share if useful to anyone.skevy commentedon Mar 15, 2026
We're hitting the same bug from the Agent SDK side, triggered by
PreToolUsehooks rather than SubAgent progress.Repro:
query()with streaming input +canUseTool+ anyPreToolUsehook (evenecho noop) + multi-turn conversation. No SubAgents involved.Turn 1: Claude calls ToolSearch → AskUserQuestion → user answers → Claude responds.
Turn 2: user sends follow-up message.
The
PreToolUsehook progress entries fork from the ToolSearchtool_usenode, orphaning the AskUserQuestion round-trip and Claude's response — identical tree shape to the SubAgent case:getSessionMessagesfollows Branch B, dropping 4-5 messages from Branch A.Key isolation: the same
query()call without aPreToolUsehook produces a clean chain (0 orphans). Adding evenecho noopas an async PreToolUse hook triggers the fork.spawnClaudeCodeProcessis not required — the default spawn reproduces it too.This confirms the root cause is the same: progress entries (from any source — SubAgent, PreToolUse hooks, etc.) that branch from a tool_use node and get flushed after the main conversation chain has advanced.
skevy commentedon Mar 15, 2026
Minimal standalone repro: https://github.com/skevy/claude-code-pretooluse-chain-fork-repro
Single file, uses only
query()+canUseTool+ a trivial asyncPreToolUsehook (echo noopin.claude/settings.json). Two turns. Verifies the JSONL chain and asserts thatgetSessionMessagesdrops the AskUserQuestion messages.junaidtitan commentedon Apr 3, 2026
SubAgent progress chains overtaking the main chain is caused by progress tick messages creating parasitic forks in the parentUuid tree. When the chain walker follows the wrong branch, the main conversation disappears.
Cozempic v1.4.1 fixes this directly:
progress-collapseremoves all progress tick messages, and thedoctorcommand'szombie-teamscheck detects stalled subagent chains._relink_parent_chain()repairs broken links after any pruning.pip install cozempic && cozempic initRun
cozempic doctorto diagnose subagent chain issues.khadimhussain0 commentedon Apr 7, 2026
Adding a CLI-only reproduction on Linux — same root cause, different trigger path.
Environment: Claude Code CLI 2.1.94, Linux (Debian), Opus 4.6
Session stats: Single session, 5,760 lines, 2,140 user messages, ~12MB JSONL, 3 compact boundaries, 24 orphan leaf nodes.
Trigger: In my case the orphan branches come from
queue-operationentries (subagent task notifications withenqueue/dequeue) combined with multiple compactions. Each compaction resets the main chain but doesn't prune old branches. Theorphan leaves accumulate at lines scattered across the file (3533, 3916, 4513, 5685, etc.), while the actual latest conversation is
at line 5750.
Behavior:
--resumeand--continuefrom a second terminal consistently picks an older leaf (around line 3533, betweencompact boundary 1 and 2) instead of the latest leaf (line 5750, after compact boundary 3). The parent chain from the correct leaf
traces cleanly back to the latest
compact_boundary— the data is intact, just unreachable via resume.Key difference from other reports: This isn't a VS Code reload or process crash — it happens on a normal
--resumefrom afresh terminal. The JSONL was never interrupted or corrupted. The branching just accumulated naturally over a multi-day session
with background tasks.
Confirms this affects CLI on Linux, not just VS Code on macOS. The
platform:macosandplatform:vscodelabels may be too narrow.github-actions commentedon May 20, 2026
Closing for now — inactive for too long. Please open a new issue if this is still relevant.
github-actions commentedon Jul 10, 2026
This issue has been automatically locked since it was closed and has not had any activity for 7 days. If you're experiencing a similar issue, please file a new issue and reference this one if it's relevant.