[BUG] Desktop app: 30-day retention sweep deletes the only copy of Desktop transcripts, leaving unopenable ghost entries in the session list

Status Fixed / completed
Reported on v2.1.219
Maintainer reply None cached
Activity 3 comments · opened Jul 25, 2026 · closed Aug 25, 2026

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

  1. "This only affects the CLI" is not correct. Desktop users are exposed identically, because Desktop's transcripts are the CLI transcripts.
  2. 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.json at C:\Program Files\ClaudeCode\ or C:\ProgramData\ClaudeCode\
  • no HKLM\SOFTWARE\Policies\ClaudeCode and no HKCU\SOFTWARE\Policies\ClaudeCode
  • ~/.claude/remote-settings.json is {}
  • no ~/.claude/settings.json existed 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:

  1. 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.
  2. 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.
  3. Surface it. cleanupPeriodDays is 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.
  4. Desktop-specific: keep the two stores consistent. Either sweep the Desktop metadata alongside the transcript, or have the Desktop session list detect a missing cliSessionId target 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

  1. Use the Claude Code Desktop app on Windows to run a session in some project directory.
  2. Observe the two stores:
  • %APPDATA%\Claude\claude-code-sessions\<accountId>\<workspaceId>\local_<sessionId>.json — note it contains no message content, and note its cliSessionId value.
  • ~/.claude/projects/<project-slug>/<cliSessionId>.jsonl — note this holds the entire transcript.
  1. Leave that session untouched for longer than cleanupPeriodDays (default 30), then start Claude Code so the startup sweep runs.
  2. The .jsonl is deleted. The local_<sessionId>.json is not.
  3. 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

View original on GitHub ↗

This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗