Deleted/archived sessions reappear as empty 'Session not found on disk' ghosts after desktop update
What happened
After a Claude Desktop app update (Windows, app version ~2.1.209 → 1.22209.0), a large number of previously deleted and archived sessions reappeared in the sidebar as empty "Session not found on disk" ghosts — each with its original title intact but no content, offering only Archive / Delete. In my case 477 of 606 sidebar entries were in this state.
Root cause (traced on disk)
The sidebar is built from per-session index stubs at:%APPDATA%\Claude\claude-code-sessions\<guid>\<guid>\local_*.json
These stubs store only a title + config — not the conversation. The conversation lives in a transcript .jsonl under ~/.claude/projects\.
Two things combine to produce the ghosts:
- Transcript retention prunes transcripts but not the index stub. With the default
cleanupPeriodDays(30), transcripts older than the window are auto-deleted, but the matchinglocal_*.jsonsidebar stub is left behind, orphaned. - "Delete" writes no tombstone. Inspecting the stubs, there is no
deletedAt/archivedAtfield on any entry (0 of 606). So a deleted session is only hidden from the current view; nothing on disk records that it was deleted.
When an update rebuilds the sidebar from the stub files on disk, every orphaned/deleted stub is resurfaced. They render empty ("Session not found on disk") because their transcript was pruned long ago, and some previously-archived sessions come back into the active list because the rebuild didn't re-honor isArchived.
Evidence from my machine: all ghost entries were created on/before ~July 10 (transcripts aged out); all live entries were recent (transcript still present); zero ghosts were created in the days around the update — i.e. the update didn't delete anything, it re-exposed already-orphaned stubs.
Impact
- Sidebar becomes flooded with empty entries (hundreds), burying real sessions.
- Users can't tell resurfaced ghosts from a genuine data-loss event without inspecting disk. It reads alarmingly like lost work.
- Re-deleting them by hand doesn't stick across the next update, since the underlying stub persists.
Suggested fixes
- Write a deletion tombstone (or actually remove the
local_*.jsonstub) when a session is deleted, so a rebuild can't resurface it. - When rebuilding the sidebar, skip stubs whose transcript is missing (or garbage-collect orphaned stubs whose transcript was pruned by retention), instead of showing them as ghosts.
- Preserve
isArchivedacross the rebuild.
Workaround
A stub whose transcript is missing can be safely removed (it holds no conversation) — this is what the UI "Delete" does. I scripted a dynamic sweep (match stub ids against transcripts on disk; quarantine the unmatched) to bulk-clear them, but it has to be re-run after each update because of cause #1 above.
Environment
- OS: Windows 11
- Claude Desktop, updating from app v2.1.209 to v1.22209.0