[BUG] Desktop app renders an older branch of the session transcript, hiding the most recent days (data intact on disk)

Status Open
Reported on v2.1.238
Maintainer reply None cached
Activity 0 comments · opened Sep 4, 2026

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?

The desktop app stops rendering conversation history at a point ~3 days in the past, while the transcript on disk contains everything up to the present. Affects multiple conversations. Two app updates (2026-09-03 and 2026-09-04) did not fix it.

This is NOT data loss. Counting entries by timestamp in the session JSONL:

| Session | 2026-09-02 | 2026-09-03 | 2026-09-04 |
|--------------|-----------:|-----------:|-----------:|
| 698952e6… | 10,381 | 7,130 | 705 |
| 76af434c… | 0 | 5,646 | 4,332 |

27,594 entries from the last three days are present on disk and not displayed.

The parent chain is intact — walking parentUuid backwards from the newest entry terminates cleanly at parentUuid: null. No dangling reference, no corruption.

What I did find: the transcript is not a single chain but a FOREST. /compact and session resumes create multiple roots and thousands of leaves in one file. In the affected session, the branch containing the newest message is NOT the largest branch:

| | 698952e6… (affected) | 76af434c… (control, renders fine) |
|---|---|---|
| entries | 60,050 | 9,841 |
| roots | 5 | 1 |
| leaves | 2,110 | 332 |
| branch holding newest message | 9,340 · 09-02 → 09-04 | 5,045 · 09-03 → 09-04 |
| largest branch | 13,914 · 08-21 → 09-02 | 5,045 · 09-03 → 09-04 |
| largest == newest? | NO | YES |

In the affected session the largest branch ends on 09-02 — which is exactly where the UI stops rendering. In the control session largest and newest are the same branch, and it displays correctly.

HYPOTHESIS (not proven — I cannot see the renderer source): the client selects the branch to render by size, or by an arbitrary/older leaf, instead of by the most recent leaf. When /compact or a resume forks the transcript, the pre-fork branch can remain larger than the post-fork one, and the user is shown the stale branch with no indication that newer content exists.

What Should Happen?

The app should render the branch containing the most recent message.

If multiple branches are retained in the transcript, the UI should indicate that other branches exist rather than silently displaying a stale one. Right now the failure is indistinguishable from data loss from the user's point of view.

Error Messages/Logs

No error is shown. The failure is silent — the conversation simply appears to end at an older date.

Steps to Reproduce

1. Use one conversation across several days, long enough that /compact triggers at least once (this forks the transcript and creates a new root).
2. Open that conversation in the desktop app (Code tab).
3. Scroll up — history stops at an older date, days before the most recent message.
4. Run the script below against that session's JSONL:

   python3 branches.py ~/.claude/projects/<project>/<session-id>.jsonl

5. Observe that the largest branch's end date matches exactly where the UI stopped, while newer entries exist on a different, smaller branch.

Script (reads only timestamps and parent links — prints no message content):

import sys, json
from collections import defaultdict

parent, ts, children = {}, {}, defaultdict(list)
with open(sys.argv[1], errors='ignore') as fh:
    for ln in fh:
        try: d = json.loads(ln)
        except: continue
        u = d.get('uuid')
        if not u: continue
        p = d.get('parentUuid')
        parent[u] = p; ts[u] = (d.get('timestamp') or '?')[:19]
        if p: children[p].append(u)

leaves = [u for u in parent if not children.get(u)]
roots  = [u for u, p in parent.items() if p is None or p not in parent]

def chain(leaf):
    seen, cur = set(), leaf
    while cur and cur in parent and cur not in seen:
        seen.add(cur); cur = parent[cur]
    return seen

newest  = max(leaves, key=lambda u: ts[u])
biggest = max(leaves, key=lambda u: len(chain(u)))
cn, cb  = chain(newest), chain(biggest)
dn, db  = sorted({ts[u][:10] for u in cn}), sorted({ts[u][:10] for u in cb})

print(f"entries={len(parent)} roots={len(roots)} leaves={len(leaves)}")
print(f"newest-message branch: {len(cn):6} · {dn[0]} -> {dn[-1]}")
print(f"largest branch       : {len(cb):6} · {db[0]} -> {db[-1]}")
if len(cb) > len(cn):
    print("MISMATCH: largest branch ends before the newest message")

PREDICTION: conversations that print MISMATCH are the ones whose history appears truncated in the UI; conversations without it render correctly. If that prediction fails, my hypothesis is wrong.

### Claude Model

None

### Is this a regression?

Yes, this worked in a previous version

### Last Working Version

_No response_

### Claude Code Version

2.1.238

### Platform

Anthropic API

### Operating System

macOS

### Terminal/Shell

Terminal.app (macOS)

### Additional Information

CLIENT: Claude Desktop 1.46388.3 (Code tab), macOS 27.0 arm64, Node v24.12.0. Not a terminal session — I selected a terminal in the dropdown only because the field is required.

ON THE THREE SUGGESTED DUPLICATES — I checked all three; all are CLOSED and describe different failures:

  • #86851 (Desktop app: latest assistant messages stop rendering while the transcript on disk stays complete) — closest in spirit and same area, but there the failure is transient and affects only the tail, with a loading indicator left on screen. Mine is persistent and renders an entire older branch spanning days.
  • #73422 (Desktop app shows empty chat history after update/restart, transcripts intact on remote host) — there the history renders EMPTY, and the setup is remote SSH. Mine renders content, just from the wrong branch, and is fully local.
  • #79940 (Session shows "transcriptUnavailable: true") — metadata written but transcript never captured. My transcript is present and complete.

ALSO RELATED: #24304 (Conversation history missing on resume, except last message) — same symptom family and explicitly notes "the history is present in the JSONL file but not loaded by Claude Code". That report traces the cause to a BROKEN PARENT CHAIN. In my case the parent chain is intact and the failure is branch SELECTION, so this looks like a distinct failure mode within the same area. It may be worth triaging them together.

WHY THIS MATTERS: from the user's side this is indistinguishable from losing days of work. I only established that the data was safe by parsing the JSONL by hand. Most users will not do that, and will reasonably conclude their history was deleted.

View original on GitHub ↗