[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 4 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 ↗

4 Comments

claell · 2 months ago

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:

  • a Desktop-specific child/duplicate of #59248 or #62476, with the mechanism retained there; or
  • a separate Desktop consistency bug covering the surviving metadata entry and missing transcript?
BasedGPT · 2 months ago

The Desktop row can survive after the .jsonl transcript it references has been removed. The metadata file under %APPDATA%\Claude\claude-code-sessions\ holds the title and cliSessionId, 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-recovery for this file-layer check. Run python tools/diagnose.py first and follow the exact command it prints. If it finds a dangling cliSessionId, 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 ⭐ :)

claell · 1 month ago

@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?

claell · 1 month ago

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:

Fixed Claude Desktop and Cowork sessions disappearing after 30 days: the transcript cleanup now keeps desktop-written sessions while they are in the app (unless org policy manages retention); the new desktopSessionCleanupPeriodDays setting caps the exemption

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:

  • A transcript for a session started or most recently continued in Claude Desktop or Cowork is kept at any age — claude-directory § Cleaned up automatically.
  • desktopSessionCleanupPeriodDays caps that exemption rather than extending retention. Its default is 0, meaning no age limit, and a transcript is deleted only once it is older than both that value and cleanupPeriodDays — so cleanupPeriodDays acts 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.
  • The exception is a managed cleanupPeriodDays: that period applies to Desktop transcripts too, and the desktop key is ignored.

Two notes for anyone arriving here from a search:

  • On 2.1.247 and earlier the exemption does not exist and desktopSessionCleanupPeriodDays is not read at all — it appears nowhere in the 2.1.247 bundle. Desktop transcripts are swept on cleanupPeriodDays like every other transcript, which is the behaviour this issue reported. cleanupPeriodDays is the only key that protects them there.
  • Because the desktop key can only reduce retention relative to leaving it unset, "set it to a large number for safety" is the wrong instinct. The value that keeps everything is the default: don't set it.

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.