[BUG] 2.1.27 session resume logic is loosing context

Open 💬 19 comments Opened Jan 31, 2026 by rpl-james-overington2

Preflight Checklist

  • [x] I have searched existing issues and this hasn't been reported yet
  • [x] This is a single bug report (please file separate reports for different bugs)
  • [x] I am using the latest version of Claude Code

What's Wrong?

Claude Code's session resume logic is not traversing the full parent chain when constructing the API request. It appears to only load recent messages rather than the complete history.

Impact

  • High - Users lose all context when resuming sessions
  • Makes --resume effectively useless for continued work
  • Forces users to re-explain context or start new sessions

What Should Happen?

When resuming a session with --resume, Claude Code should load the complete conversation history from the session file and send it to the API. The full parent chain exists in the
JSONL file - it just needs to be traversed and included in the request.

After resume, Claude should have full context of all prior conversation and be able to reference earlier discussions, tool outputs, and decisions made during the session.

Error Messages/Logs

None - there is simply a lack of context.

Steps to Reproduce

In any session with some reasonable context, use the /context command to verify the quntity. Exit, launch claude again and /resume the previous session. /context reveals significantly less context - in some cases almost none

Claude Model

Opus

Is this a regression?

Yes, this worked in a previous version

Last Working Version

2.1.25

Claude Code Version

2.1.27

Platform

Anthropic API

Operating System

Ubuntu/Debian Linux

Terminal/Shell

Terminal.app (Ubuntu 24.04)

Additional Information

_No response_

View original on GitHub ↗

19 Comments

github-actions[bot] · 5 months ago

Found 3 possible duplicate issues:

  1. https://github.com/anthropics/claude-code/issues/15837
  2. https://github.com/anthropics/claude-code/issues/21617
  3. https://github.com/anthropics/claude-code/issues/22030

This issue will be automatically closed as a duplicate in 3 days.

  • If your issue is a duplicate, please close it and 👍 the existing issue instead
  • To prevent auto-closure, add a comment or 👎 this comment

🤖 Generated with Claude Code

rpl-james-overington2 · 5 months ago

# Claude CLI: Parent Chain Persistence Race Condition Causes Context Loss on Resume

Summary

Progress messages reference parent UUIDs before those parents are flushed to disk, creating broken parent chains that cause massive context loss when resuming sessions.

Impact

On --resume, Claude CLI traces the parent chain from the most recent message to build conversation history. When this chain hits a broken link (missing parent), only the orphaned fragment is loaded instead of the full session.

Observed: Resume loaded 4.5k tokens instead of 107k tokens (~4% of context).

Root Cause

Progress messages (hook events, tool progress, etc.) are written to the session JSONL with parentUuid references to messages that have not been persisted yet. This creates orphaned chain segments.

Example from affected session

L659 | 05:28:06 | assistant | uuid=346df931 | parent=8b6cf38a ← Old chain ends
| | (message e4124bda created in memory but never flushed)
L660 | 05:28:11 | progress | uuid=0bc43f20 | parent=e4124bda ← References missing parent
L661 | 05:28:12 | system | uuid=c5b09d7e | parent=0bc43f20 ← New orphan chain starts

Evidence

  • Affected session had 10 broken links, all progress type messages
  • Missing parents follow a pattern: created in memory, referenced immediately, never written
  • Gap between last valid parent and broken reference is typically 1–7 seconds

Reproduction

  1. Run a session with hooks that emit progress events
  2. Exit and resume the session
  3. Observe drastically reduced context (check cache_creation_input_tokens in API response)

Suggested Fix

Ensure parent messages are flushed to disk before child messages reference them, or write progress messages with the last persisted parent UUID rather than the last created parent UUID.

Workaround

Repair script that repoints broken parentUuid references to the most recent valid ancestor by timestamp.

See attached repair-sessions.py

Environment

  • Claude CLI 2.1.27
  • Sessions with frequent progress events (hooks, tool use, etc.)

Root Cause (Expanded)

CLI persistence race condition: progress messages are written with parentUuid references to messages that have not been flushed to disk yet.

tfvchow · 5 months ago

(Note: The following is entirely prepared by Claude Code, and my role is nothing more than reproducing the symptoms and directing Claude Code as a non-technical guy. Please take everything with an extra grain of salt.)

___

Session Chain Breakage in insertMessageChain / appendEntry

We've been hitting an issue where /resume loses most or all conversation history. After digging into the minified cli.js, we found what appear to be two independent mechanisms that each produce orphan parentUuid references, breaking the message chain. Both seem to remain unfixed as of v2.1.29.

All references to minified cli.js in @anthropic-ai/claude-code v2.1.29 (11,139,710 bytes). Offsets are against the stock binary unless noted otherwise. Variable names are from the minified source — the actual source names will differ.

How the session JSONL works

Claude Code persists each session as a JSONL file where every entry (user messages, assistant responses, progress events, etc.) has a uuid and a parentUuid pointing to the previous entry. This forms a singly-linked chain. On /resume, the session loader walks backward from the last entry via parentUuid references to reconstruct the conversation history for the API request.

When a parentUuid points to a UUID that doesn't exist in the file (an "orphan"), the chain walker can't find the parent and stops — everything before that point is lost. A single orphan near the start of the session means /resume recovers only the entries after the break, which can be as little as the last message.

Bug A: CIA cross-contamination from sidechain entries

File: cli.js, appendEntry method (offset 10288159)

appendEntry maintains a memoized UUID Set (we're calling it CIA, offset 10308899) for dedup and chain anchoring. Sidechain entries (e.g., prompt_suggestion subagent responses) are correctly written to separate subagent files under <session-id>/subagents/, but their UUIDs appear to be added to the shared CIA Set:

// appendEntry, offset 10289584
let $=A.isSidechain&&A.agentId!==void 0,    // $ = true for sidechain entries
    _=$?wS(RP(A.agentId)):H;                // _ = subagent file path (not main JSONL)
// ...
if(!O.has(A.uuid)){
    if(Y.appendFileSync(_,B1(A)+"\n",{mode:384}),  // writes to SUBAGENT file
       O.add(A.uuid),                                // adds to SHARED CIA Set ← BUG?
       this.remoteIngressUrl&&Wh(A))
        await this.persistToRemote(q,A)
}

jh (offset 10290727) appears to be the main persistence function. It scans messages against CIA to find the chain anchor — the last UUID already on disk — then calls insertMessageChain with that anchor as the initial parent:

// jh, offset 10290727
async function jh(A,q){
    let K=f9q(A),Y=B6(),z=await CIA(Y),  // z = shared Set (includes subagent UUIDs)
        w=[],H;
    for(let O of K)
        if(z.has(O.uuid)) H=O.uuid;      // H picks up subagent UUID as anchor
        else w.push(O);
    if(w.length>0)
        await lD().insertMessageChain(w,!1,void 0,H,q);  // main-file entry gets parentUuid=H
    return K[K.length-1]?.uuid||null
}

When H is a subagent UUID, the first entry written to the main JSONL gets parentUuid pointing to a UUID that only exists in a separate subagent file. On /resume, the chain walker hits undefined at that boundary.

Reproduction: Start a session with hooks active. Wait for a prompt_suggestion subagent to spawn (visible as a suggested prompt in the input box). Send any message. The next progress entry in the main JSONL should have parentUuid pointing to the subagent's assistant response UUID. Verify with:

# Find orphan parentUuids in main JSONL
python3 -c "
import json, sys
uuids, parents = set(), set()
for line in open(sys.argv[1]):
    e = json.loads(line.strip())
    if 'uuid' in e: uuids.add(e['uuid'])
    if 'parentUuid' in e and e['parentUuid']: parents.add(e['parentUuid'])
orphans = parents - uuids
print(f'Orphan parentUuids: {len(orphans)}')
for o in orphans: print(f'  {o}')
" ~/.claude/projects/<project>/<session>.jsonl

In our testing, every prompt_suggestion spawn produced exactly 1 orphan. The orphan UUID could be found in the corresponding subagent file under <session-id>/subagents/.

What fixed it for us: Short-circuit O.add() for sidechain entries at offset 10289644:

Old:
if(Y.appendFileSync(_,B1(A)+"\n",{mode:384}),O.add(A.uuid),this.remoteIngressUrl&&Wh(A))

New:
if(Y.appendFileSync(_,B1(A)+"\n",{mode:384}),(!$&&O.add(A.uuid)),this.remoteIngressUrl&&Wh(A))

JavaScript short-circuit: when $ is true (sidechain), !$ is false, and O.add() is never called. Main-chain entries are unaffected ($ is false!$ is trueO.add() executes).

Status: Appears unfixed as of v2.1.29.

Bug B: Spread-last override in insertMessageChain

File: cli.js, insertMessageChain (offset 10287565)

// insertMessageChain, offset 10287565
let D={
    parentUuid:J?null:X,                    // computed: correct sequential parent
    logicalParentUuid:J?w:void 0,
    isSidechain:q,
    teamName:z?.teamName,
    agentName:z?.agentName,
    userType:TIA(),
    cwd:y6(),
    sessionId:O,
    version:DeY,
    gitBranch:H,
    agentId:K,
    slug:$,
    ..._                                    // React state message — OVERRIDES parentUuid
};
await this.appendEntry(D),w=_.uuid

JavaScript object spread: later properties override earlier ones. If _ (the React state message object) has a parentUuid field, it replaces the computed sequential parent. From what we can tell, React state tracks the conversation as a flat tree where all messages are children of a single transient root node (system message / summary). That root node's UUID is never persisted to the main JSONL.

In practice, _.parentUuid often seems to match the computed X because React state and the persistence layer track the same chain — making this bug latent under normal conditions. It appears to become the dominant phantom source when Bug A shifts the computed parent to a subagent UUID (which _.parentUuid would not match), or when any other mechanism causes X and _.parentUuid to diverge.

What fixed it for us: Spread-first at offset 10287565:

Old:
let D={parentUuid:J?null:X,logicalParentUuid:J?w:void 0,isSidechain:q,
  teamName:z?.teamName,agentName:z?.agentName,userType:TIA(),cwd:y6(),
  sessionId:O,version:DeY,gitBranch:H,agentId:K,slug:$,..._}

New:
let D={..._,parentUuid:J?null:X,logicalParentUuid:J?w:void 0,isSidechain:q,
  teamName:z?.teamName,agentName:z?.agentName,userType:TIA(),cwd:y6(),
  sessionId:O,version:DeY,gitBranch:H,agentId:K,slug:$}

Computed properties now appear after the spread, so they always win regardless of what _ carries.

Status: Appears unfixed as of v2.1.29.

Summary of fixes

| Fix | Bug | Location |
|-----|-----|----------|
| CIA sidechain filter in appendEntry | A | offset 10289644 |
| Spread-first in insertMessageChain | B | offset 10287565 |

Both target code that appears unchanged between v2.1.27 and v2.1.29 — seemingly unfixed as of v2.1.29.

Verification

Post-fix JSONL chain integrity can be verified using the orphan-detection script above. Expected results with the CIA sidechain filter and spread-first fixes applied:

  • 0 orphan parentUuids — every parentUuid in the main JSONL resolves to an entry in the same file
  • 1 connected component — the chain walker reaches all user/assistant messages from the tail entry (minus architectural side-branches from hook progress entries, which are expected dead-end leaves)

New chat creation, /resume, and cross-session switching all function correctly for us with fixes applied.

tamasarpad · 5 months ago

This happens on 2.1.29 as well. My conversations are broken on resume.

Workaround Repair script that repoints broken parentUuid references to the most recent valid ancestor by timestamp. See attached repair-sessions.py

Unfortunately the workaround script attached to this script broke the conversations even more, claude -c doesn't start after applying it.

nakamurau1 · 5 months ago

Additional reproduction report

I encountered the same issue on v2.1.29.

Environment

  • Claude Code: v2.1.29
  • OS: macOS (Darwin 23.6.0)

Symptoms

  • Resumed a session using claude -r <session-id>
  • Saw "Baked for 40s" message
  • Most conversation context was lost (displayed as "No content")
  • I had NOT manually run /compact before this

Verification

Ran the orphan UUID check script on my session file:

Total entries with UUID: 1772
Root entries (no parent): 4  ← Should be 1

Chain sizes from each root:
  Line 1:    391 entries
  Line 416:  482 entries  
  Line 929:  566 entries
  Line 1538: 120 entries  ← Only this chain was loaded on resume

Orphan parentUuids: 4
Unreachable entries: 213 (12%)

The session was fragmented into 4 disconnected chains. On resume, only the last chain (120 entries, ~7% of total) was accessible, causing massive context loss.

This confirms the Bug A (CIA cross-contamination) and Bug B (spread-last override) described in this issue.

rpl-james-overington2 · 5 months ago

Verification and Repair Script Update

@tfvchow - Analysis Confirmed

I've independently verified both bugs you identified in v2.1.29:

Bug A (CIA cross-contamination): Confirmed at offset ~10289644. The appendEntry method adds sidechain UUIDs to the shared Set (O.add(A.uuid)) even when writing to subagent files. The jh function then uses this contaminated Set for chain anchoring.

Bug B (spread-last override): Confirmed at offset ~10287565. The insertMessageChain uses:

let D={parentUuid:J?null:X, ..._, /* spread last */}

Allowing React state to override computed parentUuid.

Both patterns exist exactly as described.

---

@nakamurau1 - Reproduction Validated

I ran a similar analysis on 699 sessions:

| Metric | Value |
|--------|-------|
| Sessions with orphan UUIDs | 8 (1.1%) |
| Total orphan parentUuids | 28 |
| Unreachable entries | 2,371 (1.6%) |
| Orphans traceable to subagent files | 75% |

Your chain fragmentation pattern (4 disconnected chains, only last loaded on resume) matches exactly what Bug A predicts.

---

@tamasarpad - Repair Script Clarification

The repair script attached to this issue works correctly. I've been running it automatically on every session resume with no issues:

  • 18 sessions repaired with backups created
  • All repaired sessions show 0 orphans post-repair
  • No JSON corruption or malformed entries

If claude -c failed after running the script, the cause was likely:

  1. Pre-existing corruption beyond orphan parentUuids
  2. Backup files exist (.jsonl.repair-bak) - restore with: mv session.jsonl.repair-bak session.jsonl

The script only performs targeted string replacement on "parentUuid":"<orphan>" references, repointing them to the most recent valid ancestor by timestamp. It does not restructure entries or modify message content.

---

Automated Repair Integration

For anyone wanting to run repairs automatically on session resume, I've integrated this into my launch wrapper. The script:

  1. Creates backups before any modification
  2. Uses a completion cache to skip already-processed sessions
  3. Only fixes orphan parentUuid references (conservative approach)
  4. Reports repairs made on startup

Happy to share the integration approach if useful.

larssn · 5 months ago

Please work on fixing this. Hours of prompting getting lost is not enjoyable.

lukens · 5 months ago

I've finally twigged this is the issue I've been having too. It's super annoying.

You resume a conversation and in only remembers the last couple of posts. Nothing else. Not what plan it was working on. If you were working on a plan, entering and existing helps a bit to at least reboot the plan into memory, it seems. But Claude still remains absolutely oblivious to anything more than a comment back.

I've wasted so long thinking I'd been resuming the wrong chat, etc.

This is a serious bug, and definitely needs prioritising immediately. I'm amazed if nobody at Anthorpic is hitting the issue themselves.

Also, Claude is very annoying if you ask "don't you remember what you were working on?" and rather than saying "no", it just hallucinates what it might have been working on and starts "continuing" what it was doing.

tfvchow · 5 months ago
Please work on fixing this. Hours of prompting getting lost is not enjoyable.

If you don't mind, could you please tell me why you're downvoting my comment? Although all the technical details and writing are done by Claude Code, I did put a lot of effort in replicating the symptoms, experimenting with the fixes suggested by it, and ensuring the write-up is at least readable.

It is far from perfect, but that's what I can give as an amateur, and I don't see any further loss of context in resuming after applying the fixes.

---

IIRC, chats aren't gone for good. It is their UUIDs got messed up in the JSONL files. You can certainly ask Claude Code to write a script to get them back in order so you can resume the chat proper again.

larssn · 5 months ago
> Please work on fixing this. Hours of prompting getting lost is not enjoyable. If you don't mind, could you please tell me why you're downvoting my comment? Although all the technical details and writing are done by Claude Code, I did put a lot of effort in replicating the symptoms, experimenting with the fixes suggested by it, and ensuring the write-up is at least readable. It is far from perfect, but that's what I can give as an amateur, and I don't see any further loss of context in resuming after applying the fixes. IIRC, chats aren't gone for good. It is their UUIDs got messed up in the JSONL files. You can certainly ask Claude Code to write a script to get them back in order so you can resume the chat proper again.

It wasn't my intention to offend and I can understand how down-votes sting.
But I'll give you some feedback:

The OP reports one bug: session resume loses context. You've posted two
speculative "bugs" derived from AI analysis of minified code.

This pollutes the issue. Maintainers now have to evaluate whether your
findings are real or AI hallucinations from pattern-matching obfuscated
variable names. Minified code analysis is fragile and these "bugs" may not exist.

The OP gave maintainers everything they need: clear repro steps and a known
regression point (2.1.25 -> 2.1.27). With source access, they can diff those
versions and find the actual bug in minutes.

More useful: confirm the repro, add data points, or help bisect the commit. 🙂

_Sorry for adding more noise to this topic._

tfvchow · 5 months ago
> > Please work on fixing this. Hours of prompting getting lost is not enjoyable. > > > If you don't mind, could you please tell me why you're downvoting my comment? Although all the technical details and writing are done by Claude Code, I did put a lot of effort in replicating the symptoms, experimenting with the fixes suggested by it, and ensuring the write-up is at least readable. > It is far from perfect, but that's what I can give as an amateur, and I don't see any further loss of context in resuming after applying the fixes. > IIRC, chats aren't gone for good. It is their UUIDs got messed up in the JSONL files. You can certainly ask Claude Code to write a script to get them back in order so you can resume the chat proper again. It wasn't my intention to offend and I can understand how down-votes sting. But I'll give you some feedback: The OP reports one bug: session resume loses context. You've posted two speculative "bugs" derived from AI analysis of minified code. This pollutes the issue. Maintainers now have to evaluate whether your findings are real or AI hallucinations from pattern-matching obfuscated variable names. Minified code analysis is fragile and these "bugs" may not exist. The OP gave maintainers everything they need: clear repro steps and a known regression point (2.1.25 -> 2.1.27). With source access, they can diff those versions and find the actual bug in minutes. More useful: confirm the repro, add data points, or help bisect the commit. 🙂 _Sorry for adding more noise to this topic._

You're right. That's not the ideal way to report, and the search space could have been much smaller if I had taken the diff between two versions. I will definitely include it and "unminify" the code that if I ever have to do it again.

However, I stand by posting here to share with anyone looking for a workaround while waiting. I will just say not being able to fully resume a chat is far worse than receiving a down-vote, and I just don't have to deal with it, thanks to the fixes I shared.

Anyways, lesson learned and appreciate your thoughts. Hope someone will fix it soon.

lcpj · 5 months ago

Just happened to me. 2.1.31. Json is there, I can see the conversation on resume window, but in this particular case, ALL conversation was lost.
My last command was /quit , no crash and graceful exit

gonewx · 4 months ago

This is a frustrating regression — session resume should preserve the full conversation tree, not just a summary.

The deeper issue is that resume is doing too much "optimization" — it compacts or drops context during resume to save tokens, but in doing so loses the nuance and decisions from earlier in the session. You end up with a session that technically continues but has amnesia about what you discussed.

For a more reliable workflow, I have been using Mantra alongside Claude Code — it captures the full session state independently, so even if /resume loses context, you can browse the complete history in Mantra and manually re-inject the critical parts into your new session.

Not ideal that we need a workaround for this, but it beats re-explaining 30 minutes of context from scratch.

syabro · 4 months ago

thanks guys! my 20mb conversation bloody saved!

Overall just ask claude to find your missing conversation jsonl, give this issue url and ask to check for broken parentIds. Should wor

Postmortem: Claude Code Session Context Loss

Impact: /resume returned empty context — 97.1% of session history unreachable (280 of 9,789 entries visible from tail).

Root Cause: Broken parentUuid chains in session JSONL. Two entries referenced parentUuid values that didn't exist in the file. The session is a linked list walked from tail — a single broken link cuts off everything before it. Similar issue described in #22107, exact cause of orphan UUIDs unknown.

Detection: /resume couldn't find resume-builder context. JSONL analysis revealed 2 orphan parentUuids — chain from tail only reached 280 entries.

Fix: fix-session.py — repoints orphan parentUuids to nearest previous entry with a valid UUID. Backup before modification, atomic write (tmp + rename), ran while Claude Code wasn't writing to the file.

Result: Full chain restored, /resume works.

ilaikim99 · 4 months ago

The session JSONL is getting fragmented into disconnected chains by orphan parentUuid references — on resume, the walker hits the first broken link and only loads the tail fragment. There's a Python diagnostic script to count orphans and a repair script to repoint them. Root cause analysis + scripts: https://cacheoverflow.dev/blog/u-0oY5DW

gvelesandro · 4 months ago

@rpl-james-overington2 I'm researching failures where session resume silently drops most of the working context instead of carrying the full task state forward. Your report is one of the clearest public examples because --resume can load only a small tail fragment of a much larger session, leaving the agent with partial history and no reliable sense of the earlier plan.

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 agent was trying to do before resume, what context was missing after resume, where that missing context should have lived, what workaround you used, and what it cost. No pitch, just research.

ThatDragonOverThere · 4 months ago

Still reproducible on v2.1.76, Windows 11, agent mode.

Same root behavior: resumed agent has zero knowledge of prior session work. In my case the agent had a MEMORY.md file with dozens of established decisions (Chrome profile paths, tested login strategies, which bat files to run). On resume, none of it was loaded. The agent re-researched everything from scratch.

The specific symptom I can add: even mid-session, spawning a sub-agent to "update memory files" and then continuing doesn't work. The sub-agent updates the files but the parent agent doesn't re-read them, so the loop of context loss continues.

The practical consequence in my case: time-critical automation (7:00 AM permit drop on recreation.gov, one shot per day) failed because the agent couldn't remember what it had already built and tested. Two months of automation work produced zero live value in the one window that mattered.

The PostCompact hook in v2.1.76 doesn't address this — --resume appears to be a separate code path that doesn't inject MEMORY.md or agent-specific instructions at startup.

Cross-referencing: https://github.com/anthropics/claude-code/issues/34661 (filed this issue specifically for the --resume + MEMORY.md case)

junaidtitan · 3 months ago

Session resume losing context usually happens because the JSONL file has grown beyond what the chain walker can cleanly reconstruct. Progress entries create parasitic forks in the parentUuid chain, and orphaned tool_results confuse the loader.

Cozempic v1.4.1 fixes both: progress-collapse removes all progress tick messages, and the executor's _relink_parent_chain() automatically repairs broken parent links. fix_orphaned_tool_results() removes tool_result blocks whose matching tool_use was lost.

Run before resuming: cozempic treat <session> -rx standard --execute

Or install the guard for auto-protection: pip install cozempic && cozempic init

The doctor command also diagnoses these issues: cozempic doctor

duusikii · 2 months ago

Hitting this with Claude Code v2.1.117 in claude -p --resume <uuid> mode when invoked by OpenClaw's Telegram bridge.

OpenClaw deterministically passes the same --resume <uuid> per chat_id across turns, but Claude CLI silently rotates to a new session UUID after each -p run, so the next turn's --resume lands on a stale/empty session and prior conversation context is lost.

Reproducible across consecutive Telegram messages within the same chat. +1 — would really appreciate a fix that makes -p --resume <uuid> either:
(a) write the new turn back into the same UUID, or
(b) reliably error rather than silently creating a new session.