[BUG] Desktop app: 30-day retention sweep deletes the only copy of Desktop transcripts, leaving unopenable ghost entries in the session list
Disclosure up front
This is related to but distinct from #59248 (CLI, open, labelled data-loss) and #62476. The Desktop-specific report #62959 described the visible symptom ("ghost entries that error on click") but was closed as stale without the mechanism being identified. I am filing separately because the Desktop failure mode has a different root cause path and a different fix, and because Desktop is currently assumed in those threads to be unaffected. Happy for this to be folded into #59248 if triage prefers — but please don't close it without the Desktop mechanism below being recorded somewhere.
What's Wrong?
The Desktop app stores no transcript of its own. The 30-day retention sweep deletes the only copy, and the Desktop session list is left pointing at nothing.
Verified on Claude Code 2.1.219, Desktop app on Windows 11.
The Desktop app's per-session file lives at:
%APPDATA%\Claude\claude-code-sessions\<accountId>\<workspaceId>\local_<sessionId>.json
This file is metadata only. In my case the entire ~99 KB payload was remoteMcpServersConfig; there is no messages key and no message content anywhere in it. Top-level keys are:
sessionId, cliSessionId, cwd, originCwd, lastFocusedAt, createdAt, lastActivityAt,
model, effort, isArchived, title, titleSource, permissionMode, remoteMcpServersConfig,
alwaysAllowedReasons, sessionPermissionUpdates, classifierSummaryEnabled,
reportFindingsCard, spawnSeed
The actual conversation is referenced indirectly via cliSessionId, which resolves to:
~/.claude/projects/<project-slug>/<cliSessionId>.jsonl
In my session that .jsonl is 839 KB of real transcript, against a 99 KB metadata file that contains none of it.
The consequence: the retention sweep covers ~/.claude/projects/. %APPDATA%\Claude\claude-code-sessions\ is not among the documented swept paths. So once the sweep runs:
- the transcript — the only copy — is hard-deleted
- the Desktop session-list entry survives, because it lives outside the swept tree
- the user sees an intact session list where every entry older than the retention window errors on click
That is exactly the symptom in #62959. It isn't a separate bug; it's the retention sweep, with a worse presentation than the CLI has, because the CLI at least shows an empty list rather than a list of entries that lie.
Two things this changes about the existing threads
- "This only affects the CLI" is not correct. Desktop users are exposed identically, because Desktop's transcripts are the CLI transcripts.
- Backup advice in #59248 is incomplete for Desktop users. Mirroring
%APPDATA%\Claude\claude-code-sessions\protects nothing — it is metadata.~/.claude/projects/is the only copy of the conversation.
No policy override was involved
To pre-empt the "someone configured this" reading — on this machine:
- no
managed-settings.jsonatC:\Program Files\ClaudeCode\orC:\ProgramData\ClaudeCode\ - no
HKLM\SOFTWARE\Policies\ClaudeCodeand noHKCU\SOFTWARE\Policies\ClaudeCode ~/.claude/remote-settings.jsonis{}- no
~/.claude/settings.jsonexisted at all until I created one
This is the shipped default.
What Should Happen?
Any one of these would have prevented this. I found it by accident, with days to spare on a fresh install:
- Don't hard-delete by default. Either default the sweep off, or prompt explicitly on first run. Silent destruction of user data should not be an out-of-the-box behaviour.
- Move to quarantine instead of
unlink(). A~/.claude/trash/with its own longer expiry turns an irreversible loss into a recoverable one at near-zero cost. - Surface it.
cleanupPeriodDaysis currently documentation-only. Show the effective value and the next sweep time in/status, so a user can confirm their setting is actually being read — misspelling a key that controls deletion currently fails silently and destructively. - Desktop-specific: keep the two stores consistent. Either sweep the Desktop metadata alongside the transcript, or have the Desktop session list detect a missing
cliSessionIdtarget and show the entry as expired rather than erroring. A list of entries that all fail on click is the worst of both options.
On transparency: under GDPR Art. 4(2), erasure is explicitly a form of processing. A destructive default with no first-run notice, no in-product way to view the retention setting or the next sweep, and no recovery path is at minimum a transparency problem — irrespective of the fact that the bytes sit on the user's own disk. I raise this not as a threat but because it should raise the priority of item 1 above.
Steps to Reproduce
- Use the Claude Code Desktop app on Windows to run a session in some project directory.
- Observe the two stores:
%APPDATA%\Claude\claude-code-sessions\<accountId>\<workspaceId>\local_<sessionId>.json— note it contains no message content, and note itscliSessionIdvalue.~/.claude/projects/<project-slug>/<cliSessionId>.jsonl— note this holds the entire transcript.
- Leave that session untouched for longer than
cleanupPeriodDays(default 30), then start Claude Code so the startup sweep runs. - The
.jsonlis deleted. Thelocal_<sessionId>.jsonis not. - Open the Desktop session list: the session is still listed, with its title intact. Click it — it errors, and the conversation is unrecoverable.
For anyone who wants to check whether this has already happened to them without waiting 30 days: look for session directories or Desktop metadata entries whose corresponding <cliSessionId>.jsonl no longer exists.
Environment
- Claude Code: 2.1.219
- Surface: Desktop app (Code tab), Windows
- OS: Windows 11, 10.0.26200
cleanupPeriodDays: unset (default 30) at time of discovery
4 Comments
Following up with the current upstream context, because this Desktop-specific report has not received a triage signal and is now within the repository's inactivity window.
The underlying retention/data-loss problem remains active in #59248 and #62476. The earlier Windows/Desktop symptom report #62959 was closed as stale. This issue adds the missing mechanism: Desktop keeps a session-list record that points to the transcript under
~/.claude/projects/, so the retention sweep can remove the only transcript while leaving a visible entry that fails when opened.I am not asking for a separate implementation if maintainers prefer consolidation. An explicit duplicate link to the chosen root issue would be useful. Otherwise, please keep this as the Desktop-specific variant so the two-store failure mode and the misleading ghost entries do not disappear with another automated closure.
Could this be triaged as either:
The Desktop row can survive after the
.jsonltranscript it references has been removed. The metadata file under%APPDATA%\Claude\claude-code-sessions\holds the title andcliSessionId, while the conversation history lives under~/.claude/projects/. That is why an old entry can remain visible but fail when opened.I built
BasedGPT/claude-code-session-recoveryfor this file-layer check. Runpython tools/diagnose.pyfirst and follow the exact command it prints. If it finds a danglingcliSessionId, review the backup-search or VSS route it gives you before concluding that the transcript cannot be recovered. The toolkit cannot stop the upstream retention sweep, but it can separate a missing transcript file from a broken metadata link.For prevention, back up both
%APPDATA%\Claude\claude-code-sessions\and~/.claude/projects/; the first preserves the Desktop session list data, while the second holds the conversation history. If you restore a transcript, run the diagnosis again so the metadata and file link are checked together.Hope this helps, if my tools are able to help you, would appreciate a ⭐ :)
@codebytere-ant can you give some details what has been completed here, what the current behavior is, etc.? Is this properly addressed now? Can I trust Claude Desktop again with my data?
Recording what closed this, since the closure came without detail and the mechanism was the thing I asked to have written down somewhere.
The fix shipped in 2.1.248, one release after this was closed. From the changelog:
So the two-store problem described above was addressed by exempting the transcript rather than by teaching the sweep about the Desktop session record. The current documented behaviour:
desktopSessionCleanupPeriodDayscaps that exemption rather than extending retention. Its default is0, meaning no age limit, and a transcript is deleted only once it is older than both that value andcleanupPeriodDays— socleanupPeriodDaysacts as a floor and no value of the desktop key can shorten retention below it. Scope is user or managed; project and local settings ignore it. settings-reference § desktopSessionCleanupPeriodDays.cleanupPeriodDays: that period applies to Desktop transcripts too, and the desktop key is ignored.Two notes for anyone arriving here from a search:
desktopSessionCleanupPeriodDaysis not read at all — it appears nowhere in the 2.1.247 bundle. Desktop transcripts are swept oncleanupPeriodDayslike every other transcript, which is the behaviour this issue reported.cleanupPeriodDaysis the only key that protects them there.One thing the documentation does not cover. The changelog conditions the exemption on the sessions being "in the app", and neither the settings reference nor the directory reference mentions that condition — both state the exemption unqualified. If removing a session from the Desktop session list makes its transcript sweepable again, that is the case where the only copy still disappears, and it is the one a user cannot currently reason about from the docs. I've filed that separately as a documentation question, #91464, rather than reopening this.