Conversation history missing on resume (except last message)
Description
After upgrading from Claude Code v2.1.15 to v2.1.24+ (and later 2.1.34), some sessions only show the last message when resumed, with all prior conversation history missing. The history is present in the JSONL file but not loaded by Claude Code.
Note: Downgrading back to v2.1.15 does NOT fix the issue, indicating the corruption happened at write time, not at read time.
_Update: I'm still getting this occasionally (so it wasn't version-switch related)_
Symptoms
- Resume a session → only see the last message
- Full conversation history exists in the
.jsonlfile - Affects some sessions but not others (no clear version pattern)
Investigation
Had Claude analyze a working session vs. a broken session. Found two corruption issues in the broken session's JSONL file:
1. Snapshot messageId Collision
file-history-snapshot entries have messageId values that collide with the immediately following message's uuid, creating ID ambiguity:
- Line 53: snapshot with
messageId: "5c1312fa-..." - Line 54: user message with
uuid: "5c1312fa-..."(same ID!)
2. Broken Parent Chain Reference ⚠️ This is the critical bug
An entry references a parent UUID that doesn't exist in the file, breaking conversation chain traversal:
- Line 50:
progressentry withparentUuid: "1b366c02-..." - No entry in the file has
uuid: "1b366c02-..." - Should point to the previous entry:
uuid: "97fdd911-..."
This broken link prevents Claude Code from traversing the conversation history backwards when loading the session. When traced back from the last message, the chain stops at this broken reference after only 6 entries instead of going back through all 26 entries in the conversation.
Root Cause
Unknown what causes the corruption during writing. Possibly related to:
- Sessions being open during version upgrade
- Specific timing/race conditions
- Not all sessions affected (sporadic issue)
Workaround
Created a Nushell script that detects and fixes both issues:
- Sets snapshot
messageIdtonull(removes collision) - Fixes broken parent references to point to correct preceding entries
- Creates timestamped backups (non-destructive)
Fix script: https://gist.github.com/tennox/90ef5c803ec4b64c9fbba0f71ca1ae2e
nu fix-cc-session.nu ~/.config/claude/projects/YOUR-PROJECT/SESSION-ID.jsonl
Environment
- Upgraded from: v2.1.15
- Upgraded to: v2.1.24, then v2.1.34
- OS: Linux (NixOS)
- Pattern: Some sessions affected, others not (no clear correlation with version)
Expected Behavior
Resuming a session should load the full conversation history, regardless of which version created the session.
Actual Behavior
Only the last message is displayed; all prior history is invisible despite being present in the JSONL file.
17 Comments
Found 3 possible duplicate issues:
This issue will be automatically closed as a duplicate in 3 days.
🤖 Generated with Claude Code
Very annoying indeed, you simply get one entry in /resume, not matter if there are previous messages in the current conversation or the conversation already being multiple compacted conversation long.
<img width="994" height="340" alt="Image" src="https://github.com/user-attachments/assets/716c057c-a6c8-488b-b5ac-092f6879bf46" />
16.6 MB but one single checkpoint without the item being expandable.
Seeing the same issue on v2.1.38, macOS (Darwin 25.2.0).
Investigated the root cause — sessions-index.json is out of sync with actual session files on disk:
The missing sessions are valid .jsonl files, not corrupted — they're just not indexed. Likely happened gradually across upgrades over the past few months.
Same issue on v2.1.39, macOS Darwin 25.2.0
My case is slightly different from the original report -- my JSONL has 0 broken parentUuid references
(all chains are intact), yet resume shows essentially nothing.
Root cause in my case: Heavy use of /rewind and branching. The conversation has:
out of 693
post-compaction messages from the pre-compaction tree entirely
Additionally, sessions-index.json is missing this session entirely? (253 JSONL files on disk, only 147
indexed, 42% desync, confirming what @clarym-star reported).
The underlying issue seems to be that resume has no concept of the conversation tree. It follows one
linear path from the last message backward. With branching, this path is a tiny fraction of the actual
conversation. There's also no way to select which branch/leaf to resume from.
This is related to #19451 and #24471
Python fix script (no dependencies, works on Linux/macOS/WSL)
Ran into this on v2.1.x with heavy
/rewindusage. My session had 1,259 entries but only 199 were reachable from the last message. Root causes in my case:messageIdcollisions —file-history-snapshotentries hadmessageIdvalues identical to real message UUIDsparentUuidreference — aprogressentry pointed to a UUID that didn't exist in the fileparentUuid: null) that split the conversation into 3 unreachable subtreesAfter fixing: 513/1,195 entries reachable (remaining 682 are on abandoned
/rewindbranches, which is expected).Usage
Creates a timestamped backup before modifying. No external dependencies — stdlib only.
Script
Tested on a session with 1,259 lines, 193 branch points from
/rewind, and 3 disconnected compaction subtrees. Went from 199 → 513 reachable entries. The original issue reporter's Nushell script handles patterns 1 and 2 but not the compaction root stitching (pattern 3), which was the main cause for me.Update: I'm still getting this occasionally (so it wasn't version-switch related)
yeah I don't get how such a key feature can be broken for so long
This is a frustrating issue. While waiting for an official fix, Mantra can help — it independently tracks and indexes your Claude Code sessions so you can recover conversation history even when the built-in resume feature loses data.
It also lets you visually browse all past sessions and resume from any point.
Another reproduction on v2.1.71 (Linux) — progress entry race condition
Hit this on a long-lived session (67MB, 18K lines, 19 compaction boundaries). On resume, Claude had zero prior context — chain depth was only 2 before hitting a broken
parentUuid.Root cause in my case: A
progressentry references a parent UUID that was never written to the JSONL. This happens when a user message is queued (viaqueue-operation) while a tool call is still streaming. The sequence:The progress entry's parent (
07675861...) appears to be an in-flight message ID (streaming chunk or tool result) that was never flushed to disk — likely because the queued user message interrupted the tool execution. The next user message chains off the progress entry, inheriting the broken link, and the entire prior history becomes unreachable.I have 5 broken
parentUuidreferences in this session, all onprogressentries. The other 4 didn't cause visible issues because they weren't at the chain tip when resuming.Found this issue while reporting mine.
My UI App has a patch that if you want can be applied to Claude Code on 2.1.74.
https://github.com/wellofspirit/ClaudeUI
Adding forensic evidence from two sessions on v2.1.86 (Opus 4.6, 1M context) confirming this is still happening.
Two affected sessions
| | nah project | MCN WordPress project |
|---|---|---|
| Last turn before exit | 348,613 cached tokens | 434,086 cached tokens |
| First turn after resume | 0 cached tokens | 0 cached tokens |
| Session JSONL size | 17MB, 6,932 lines | 12MB, 7,408 lines |
| Summary events in log | 0 | 0 |
| Orphaned parentUuids | 635 / 7,108 entries | 1,628 / 7,408 entries |
Broken link analysis (nah session)
Entry types with orphaned
parentUuidreferences:These are almost entirely from Agent tool (subagent) calls. The subagent's internal messages exist in a separate context, but the results written to the main session JSONL reference
parentUuidvalues from the subagent's chain — UUIDs that don't exist in the main session file. Chain traversal hits these broken links and stops.Token-level proof of context loss
From the JSONL
usagefields:Both sessions were well within the 1M context window (35-43% used). The context should have loaded in full.
Environment
Originally filed as #40319, closing that as duplicate.
This comment was written by Claude Code.
Update: After walking the main parent chain in both sessions, it turns out the chain is fully intact (3,308 entries, zero breaks). The orphaned
parentUuidreferences are all on side branches (subagent entries) that don't affect main chain traversal.This means my sessions are affected by a different bug — the chain is fine but Claude Code still doesn't load it on resume. Reopened #40319 to track that separately.
The broken chain issue described in this ticket is still valid and likely affects other users — just not the root cause in my case.
This comment was written by Claude Code.
Thanks @tennox for the detailed write-up and root-cause analysis — it was extremely helpful for understanding what's going wrong in these JSONL files.
Building on the patterns described here, I put together a small Python tool in case it's useful to others hitting the same issue:
https://github.com/ymonster/cc_jsonl_fix
What it does:
file-history-snapshotmessageIdvalues that collide with real message UUIDsparentUuidreferences to the nearest valid message (without crossingcompact_boundarysegments)--dry-run, and runs an integrity check (orphans / cycles / duplicates) before writingRequirements: Python >= 3.10, no external deps.
One caveat: I've only been able to test it against the corrupted sessions I've personally run into, so there are almost certainly corruption patterns I haven't seen yet. If anyone tries it and it doesn't fix your file (or fixes it incorrectly), I'd really appreciate an issue on the repo with details — more real-world broken files would help me cover more cases. Hopefully this gets fixed upstream eventually, but in the meantime sharing in case it helps.
Update — hit a different failure mode of (presumably) the same root issue today.
Original report describes silent truncation on resume. What I just hit: resume succeeded, the session opened normally, but every prompt afterward got no response — effectively frozen. The 47 MB session turned out to have:
So the snapshot-vs-uuid collision path can freeze a resumed session entirely, not just shorten it. Same chain-integrity family as the original report, different externally visible symptom.
cc_jsonl_fix repaired it cleanly: https://github.com/ymonster/cc_jsonl_fix
Related but distinct variant, reported from Claude Desktop (macOS) rather than CLI
--resume.Environment: macOS 26.5.1 (build 25F80), Claude Code 2.1.235, via Claude Desktop's Claude Code tab.
Symptom: A session that had been used across several separate sittings over multiple days went missing from the Desktop app's session content — the title still appeared in the Recents list, but opening it showed only the first sitting's messages, with no indication anything later was missing.
What I found on disk:
.jsonltranscript is complete and well-formed: 8 user turns spanning multiple days, ending at the exact minute the user closed their laptop that night. I walked theparentUuidchain end-to-end — no broken links, no id collisions like the corruption described above. The file itself is intact..jsonlfile anywhere in~/.claude/projects/.... It's an index-only entry with no backing transcript file.lastActivityAtmetadata kept advancing in step with the real file's actual last-write time (matched to within ~6 seconds) on every later day the session was used. So the index knew the session was still active, but never re-synced the message list it displays past turn 2.This looks like a different root cause from the parent-chain corruption above — the source-of-truth
.jsonlwas never damaged — but the same user-facing outcome: real conversation content on disk, inaccessible through the app. Feels closer to the stalesessions-index.jsonbehavior described in #26519, just manifesting in the Desktop app's Recents list instead of CLI--resume. Happy to share the session-id / index-id pair or exact timestamps if it helps repro.Possibly the same failure class, different symptom shape and platform. Sharing in case it's useful triangulation data.
Environment
/clear) since ~2026-07-17,.jsonltranscript ~1.68MB at time of reproSymptom
claude -c -p "<prompt>"(and interactively-c) resumes the session, but the reconstructed context reflects a topic from roughly 6 weeks earlier in the transcript instead of the actual most recent exchange, even though:.jsonlfile demonstrably contains all messages up to the current moment (verified by direct read, and by watching the file's mtime/size update correctly as new turns were appended).Repro:
/clear, no new session) until the transcript file grows large.claude -c -p "In one sentence, what was the last topic we discussed?".Also independently noticed
~/.claude/history.jsonl(the input-recall log, separate file) had stopped being appended to as of the same rough time window, despite the session itself staying active well past that point. Manually repairing that file (appending well-formed entries with the correct session ID/timestamps) did not change the-cresume symptom above, so it looks like two distinct staleness bugs rather than one, but both point at some kind of stale checkpoint/leaf-node tracking on long-lived sessions rather than a read-time parsing issue.Happy to share the (redacted) transcript file directly if that'd help pin down whether it's the same broken-parentUuid-chain pattern described above, just didn't want to paste raw session content publicly.
For resume showing only the last message while the JSONL is present, please run opaque diagnosis and snapshot before changing versions.
Thanks for the detailed report. This is a good candidate for a bounded recovery check. Vetto 0.2.0-alpha.2 is available from npm and adds an experimental read-only Claude adapter. It treats Claude JSONL as opaque; it does not resume, rewrite, or reconstruct provider state. On a disposable copy, try:
npm install --global @shleddy/vetto@next;vetto rescue --adapter claude --root <CLAUDE_STATE_ROOT> --json scan; then use the exact returned key withdiagnoseandsnapshot ... --output ./vetto-recovery/session.jsonl. Please report OS, Claude/Vetto versions, sanitized JSON, and source SHA-256 before/after; never upload raw transcripts, settings, credentials, prompts, or tokens. An explicit unavailable/unsupported result is useful too.