Skip to content

Session resume corrupts JSONL: file-history-snapshot messageId collides with message uuid #36583

Description

@wzxklm

Bug Description

When resuming a session, Claude Code writes file-history-snapshot entries whose messageId values collide with existing message uuid values in the JSONL file. This causes parentUuid chain traversal to break, making a significant portion of conversation history unreachable after resume.

Evidence

Analyzed the JSONL file of the resumed session with a diagnostic script:

Metric Value
Total entries 330
messageId/uuid collisions 34
Broken parentUuid refs 0
Entries with uuid 251
Reachable from last entry 190 (75%)
Unreachable (lost) 61 (25%)

Collision pattern

Line 3:   type=user,                    uuid=842f0bfe-2c1e-44c0-8655-74ff68c02903
...
Line 217: type=file-history-snapshot,   messageId=842f0bfe-2c1e-44c0-8655-74ff68c02903  ← COLLISION

Lines 217-228 contain 12 consecutive file-history-snapshot entries, all with messageId values that duplicate earlier message uuid values. These appear to have been written during the resume operation.

Impact

  • Subagent summary output disappears — messages that were visible during the live session become unreachable after resume
  • 25% of conversation history lost — only 75% of messages are reachable via parentUuid chain traversal from the last entry
  • Data corruption at write time — downgrading versions does not fix already-corrupted files (consistent with Conversation history missing on resume (except last message) #24304 findings)

Steps to Reproduce

  1. Start a conversation in Claude Code (VS Code extension)
  2. Use the Agent tool (subagent) during the conversation — observe the summary output is displayed
  3. End the session
  4. Resume the session
  5. The subagent summary output is missing — both from the UI and from Claude's own context

Root Cause Analysis

The file-history-snapshot entries written during resume reuse the uuid of the original messages as their messageId. When the parentUuid chain is traversed, these collisions create ambiguity — the traversal can jump to a snapshot node instead of the real message node, breaking the chain and orphaning downstream messages.

Environment

  • Claude Code running in VS Code extension
  • Model: claude-opus-4-6 (1M context)
  • Platform: Linux
  • Context usage at time of issue: 7% (71.7k / 1000k) — well below compaction threshold

Diagnostic Script

import json

session_file = "path/to/session.jsonl"
entries = []
with open(session_file, 'r') as f:
    for i, line in enumerate(f):
        line = line.strip()
        if not line:
            continue
        entries.append((i + 1, json.loads(line)))

all_uuids = {}
collisions = 0
for line_no, entry in entries:
    uuid = entry.get('uuid')
    if uuid:
        all_uuids[uuid] = line_no

for line_no, entry in entries:
    msg_id = entry.get('messageId')
    uuid = entry.get('uuid')
    if msg_id and msg_id in all_uuids and msg_id != uuid:
        collisions += 1
        print(f"COLLISION at line {line_no}: messageId={msg_id[:16]}... collides with uuid at line {all_uuids[msg_id]}")

# Chain reachability
uuid_to_entry = {e.get('uuid'): e for _, e in entries if e.get('uuid')}
last_uuid = entries[-1][1].get('uuid')
reachable = set()
current = last_uuid
while current and current in uuid_to_entry:
    reachable.add(current)
    current = uuid_to_entry[current].get('parentUuid')

total_with_uuid = len(uuid_to_entry)
print(f"\nCollisions: {collisions}")
print(f"Reachable: {len(reachable)}/{total_with_uuid} ({len(reachable)*100//total_with_uuid}%)")

Related Issues

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:agentsarea:corebugSomething isn't workinghas reproHas detailed reproduction stepsplatform:linuxIssue specifically occurs on Linuxplatform:vscodeIssue specifically occurs in VS Code

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions