Resume loses most context: parentUuid chain breaks on unpersisted messages

Resolved 💬 6 comments Opened May 13, 2026 by darkhipo Closed Jun 22, 2026

Summary

When a session is resumed with --resume or /resume, most conversation context is silently lost. The parentUuid chain in the session JSONL contains references to UUIDs that were never written to the file. Walking the chain on resume stops at the first such break, making everything before it unreachable.

Impact

In my project (~200 session files), every session over 10KB has dangling parentUuid references. Typical reachability on resume is 1-10% of entries. In one representative session:

  • 5,783 entries with UUIDs
  • 22 dangling parentUuid references (pointing to UUIDs that don't exist in the file)
  • Active chain on resume: 301 entries (5.2% reachable)
  • 0 compact/summary markers (auto-compact is disabled)
  • Context bar went from ~35% to ~9% on resume

The lost context includes all tool results, reasoning, and conversation history before the first chain break. Users see a dramatically smaller context percentage after resume with no indication that content was lost.

Root cause (observed, not confirmed from source)

In the working portion of the chain, every entry's parentUuid points exactly to the previous entry — zero adjacency gaps. But at 22 specific points, an assistant or system entry's parentUuid points to a UUID that was never written to the JSONL as any entry's uuid.

The dangling system entries all have cause.code: "ECONNRESET" and retry metadata (retryAttempt, maxRetries, retryInMs). These appear to be API error records whose parent references internal conversation state that wasn't persisted.

The dangling assistant entries are normal responses whose parentUuid points to what appears to be an ephemeral message (system injection, hook output, or retry state) that was part of the API call but never appended to the JSONL.

Reproduction

Save this as diagnose_resume.py and run in your session directory (~/.claude/projects/<slug>/):

#!/usr/bin/env python3
"""Diagnose parentUuid chain breaks in Claude Code session JSONL files."""
import json, sys
from pathlib import Path

def diagnose(path):
    entries = []
    with open(path) as f:
        for line in f:
            line = line.strip()
            if line:
                try: entries.append(json.loads(line))
                except: pass

    by_uuid = {e["uuid"]: e for e in entries if e.get("uuid")}
    dangling = [i for i, e in enumerate(entries)
                if e.get("parentUuid") and e["parentUuid"] not in by_uuid]

    # Walk chain from latest entry
    last = next((e["uuid"] for e in reversed(entries) if e.get("uuid")), None)
    chain, current, visited = 0, last, set()
    while current and current not in visited:
        visited.add(current)
        obj = by_uuid.get(current)
        if not obj: break
        chain += 1
        current = obj.get("parentUuid")

    return len(by_uuid), len(dangling), chain

if __name__ == "__main__":
    d = Path(sys.argv[1]) if len(sys.argv) > 1 else Path(".")
    files = sorted(d.glob("*.jsonl"), key=lambda p: p.stat().st_size, reverse=True)
    affected = 0
    print(f"{'session':>14} {'size':>10} {'w/uuid':>7} {'dangle':>7} {'chain':>7} {'reach%':>7}")
    print("-" * 60)
    for p in files:
        if p.stat().st_size < 5000: continue
        wu, dg, ch = diagnose(p)
        if dg > 0:
            affected += 1
            pct = (ch / wu * 100) if wu else 0
            print(f"{p.stem[:14]:>14} {p.stat().st_size:>10,} {wu:>7} {dg:>7} {ch:>7} {pct:>6.1f}%")
    print(f"\n{affected} of {len(files)} sessions have dangling parentUuid refs")

Sample output from my environment (213 affected sessions):

       session       size   w/uuid  dangle   chain  reach%
------------------------------------------------------------
  f2929101-147 481,045,312      139       8       2    1.4%
  53eae523-807 53,093,787      254      35       3    1.2%
  946c4893-494 14,299,252     5783      22     301    5.2%
  1fa94892-324  9,860,472     2021      79       7    0.3%
  46c1e03f-4f3  7,065,886     2612    1031       3    0.1%

213 of 230 sessions have dangling parentUuid refs

Expected behavior

Every parentUuid in the JSONL should resolve to a UUID that exists in the same file. Messages sent to the API (system injections, hook outputs, retry state) should either:

  1. Be persisted to the JSONL, or
  2. Have their child entry's parentUuid reference a persisted entry instead

Environment

  • Claude Code 2.1.92 (Homebrew cask, Mach-O arm64)
  • macOS, Apple Silicon
  • Hooks configured (UserPromptSubmit, PreToolUse, PostToolUse, etc.)
  • Auto-compact disabled
  • Model: opus (1M context)

Notes

  • The issue is independent of /compact — no compact/summary markers exist in the affected files.
  • The chain breaks happen mid-session (seconds apart), not at session boundaries.
  • The dangling system entries consistently show ECONNRESET API errors, suggesting the retry path doesn't properly maintain the persisted chain.
  • Sessions without hooks may be less affected, but this is untested — the ECONNRESET retry entries suggest hooks are not the only cause.

View original on GitHub ↗

This issue has 6 comments on GitHub. Read the full discussion on GitHub ↗