Silent retention cleanup deletes session transcripts with no warning, opt-in, or recovery
Summary
A user actively working in Claude Code (Cursor extension, anthropic.claude-code-2.1.141-darwin-arm64) lost the ability to resume, view, or recover all Claude Code conversation transcripts in a given workspace older than the current session, including a session from the previous day, because a built-in cleanup process silently deleted those transcripts from disk. The user received no prompt, no warning, and no Trash-style soft-delete. The retention setting (cleanupPeriodDays) was never surfaced, never set explicitly, and the observed retention behavior is inconsistent with the documented 30-day default — sessions newer than 30 days are still missing. When the user discovered the loss, the Cursor resume picker presented an empty workspace history, leaving the user with no in-product hint that data had ever existed there.
Specific concerns
- No consent prompt. Cleanup runs out of the box with no install-time disclosure or first-run dialog. Users who treat their conversation history as durable working knowledge are silently mistaken about the persistence model.
- No UI surface for retention.
cleanupPeriodDaysis documentation-only. There is no UI in Cursor's Claude Code extension or the CLI to view current retention, see when next cleanup will run, see what will be deleted, or cancel a pending cleanup.
- Hard delete, no Trash. Deleted transcripts go straight to
unlink(). No soft-delete folder, no grace period, no--restorecommand, noclaude restoresubcommand. macOS Trash is bypassed.
- Observed behavior diverges from documented retention. With a documented 30-day default and today's date 2026-05-14, sessions from 2026-04-14 onward should survive. In this workspace, the most recent surviving transcript material is 2026-03-04 — 71 days ago. Either the documented default is wrong, the cleanup uses a different rule (delete-on-extension-update, delete-on-session-end, delete-on-
/clear, delete-on-cursor-restart), or there's a separate deletion path triggered by something other than the 30-day timer.
- Subagent orphan pattern is a smoking gun. When the parent
<sessionId>.jsonlis deleted but the<sessionId>/subagents/subdirectory is left behind, the result is dead weight: tens of MB of subagent transcripts that reference a parent session that no longer exists, cannot be resumed, and are not surfaced in any UI. Either the cleanup should be transactional (delete the dir AND the subagents together, OR delete neither), or both should be retained.
- Cleanup is timed to session start — the worst possible moment. Running cleanup ~12 minutes after a Claude Code session starts means a user who opens Claude Code today to look up what they discussed yesterday triggers the very cleanup that deletes yesterday's transcript. The interaction is destructive at the exact moment the user most needs the data.
- Cross-version risk. The Cursor Claude Code extension updated on 2026-05-13 (the day of the lost session). The user attempted version rollback to recover — which had no effect, because the data lives in
~/.claude/and is not version-coupled. Anthropic should clarify whether extension upgrades trigger their own cleanup, and whether transcripts written by an older extension version are at risk on first run of a newer version.
- No telemetry or audit trail. No log of what was deleted, when, or why. A user has no way to confirm what they lost or build a case for restoration.
Suggested fixes
- Surface a retention disclosure on first run; default to longer or infinite retention until the user opts in.
- Add a soft-delete tier: move deleted transcripts to
~/.claude/projects/<workspace>/.trash/for N days before unlink, withclaude restoreto recover. - Make cleanup transactional: never delete a parent
.jsonlwhile itssubagents/directory still has content. - Don't run cleanup at session-start; run it on session-end, or on an idle timer that respects the "user just opened Claude Code" signal.
- Add an explicit log of cleanup actions to
~/.claude/cleanup.logwith timestamps and deleted file list. - Expose
cleanupPeriodDays(and acleanupEnabledboolean) in a settings UI, not just via JSON. - Reconcile documented vs. actual retention behavior; if reproducible, treat it as a bug.
Reproduction conditions
- macOS 25.5.0 (Darwin)
- Cursor IDE with Claude Code extension
anthropic.claude-code-2.1.141-darwin-arm64 - A
cwdcontaining a literal space (e.g.Chrome Extentions/proj-universal-capture-cursor) — produces dual slug variants under~/.claude/projects/. - No
cleanupPeriodDaysoverride in~/.claude/settings.json. - File of interest:
~/.claude/.last-cleanup(rewritten each cleanup pass; contains a single ISO timestamp).
On-disk evidence
~/.claude/.last-cleanupupdated 2026-05-14 13:13:20 PT (today). File born 2026-05-07.- 8 orphan
<sessionId>/subagents/directories survive in the affected workspace (Feb 12 – Mar 4 content, ~15 MB, 72 files), with no parent.jsonlfor any of them. ~/.claude/file-history/confirms 6 session UUIDs were active on 2026-05-13 — none of those UUIDs have any surviving session directory or transcript anywhere in~/.claude/projects/.- User's
~/.claude/settings.jsoncontains nocleanupPeriodDayskey — running on whatever the binary default is.
Timeline (2026-05-14 PT)
| Time | Event |
|---|---|
| 12:59:22 | First Claude Code session of the day starts (a different Cursor workspace) |
| 13:01:02 | Second Claude Code session starts in the affected workspace |
| 13:13:20 | Cleanup pass runs and updates .last-cleanup |
The cleanup runs ~12 minutes after session start. The deletion of yesterday's transcripts is most plausibly attributed to this pass.
21 Comments
Found 3 possible duplicate issues:
This issue will be automatically closed as a duplicate in 3 days.
🤖 Generated with Claude Code
Thanks for the duplicate suggestions. I've reviewed the three referenced issues and want to flag this report as same bug family but with materially new evidence — please keep it open rather than auto-closing.
Cross-reference
cleanupPeriodDays: 99999was set and ignored. Lists "version upgrade reset" as suspected root cause.What this report adds that the three above do not
anthropic.claude-code-2.1.141-darwin-arm64). The three predecessors are all CLI. This widens the scope..last-cleanupmtime: the cleanup pass runs ~12 minutes after a Claude Code session starts. This is the worst possible moment — a user opening Claude Code to recall yesterday's session triggers the cleanup that deletes yesterday's session. Suggests the fix in #41458 root-cause analysis is on the right track (cleanup running before settings fully resolve, or in the wrong subprocess context).Ask
Please retain this issue and consolidate with #46621 / #41458 as part of an active investigation rather than closing as a duplicate. The new platform (Cursor extension), version (2.1.141), and timing evidence (cleanup-at-session-start, same-day-upgrade-correlation) materially advance the diagnosis. Happy to provide the full local forensic report on request.
Local forensic file path on the affected machine for cross-reference:
docs/transcripts/claude-code/2026-05-14_session-recovery-forensics.md+1, 1800+ husks on macOS, v2.1.150, default settings, May 24 cleanup wiped years of work. Have now spent 9+ hours debugging with no way to get them back. Claude code silently deleted all of my /project session jsonl files with no warning, no asking for permission. Then when claude code suggested filing a bug report here, it immediately got flagged by the bot saying it was a duplicate. So how many more people are living through this who are not being allowed to echo the issue here? This is more than an "oops". This is catastrohpic for long term development work if you aren't backing up your local files. I generally have no reason to - claude project files would have been the only reason. Who knew that anthropic would decide all of my chats with all of the logic, reasoning, decisions and why should just be deleted with no warning. I echo the user who said "whose files did they think those were anyway"?
It's wiping out MY DATA and hard work.
I hit this issue and built a recovery tool for the macOS + Time Machine case: https://github.com/garrettmoss/restore-claude-history
Some caveats re scope:
~/.claude/projects/. Doesn't touch Claude Desktop session storage (YET. Working on it.)The README also has the one-line
~/.claude/settings.jsonchange ("cleanupPeriodDays": 36500) that's supposed to defang the documented cleanup going forward. Worth setting even if you don't need recovery — (though per #41458 the setting isn't always honored, which is partly why a recovery path matters).Open to suggestions/fixes if needed.
Lost months of chat history on a personal project, and much else, on many different Claude "updates".
Filed #62250 with the same loss; folding it here as a duplicate. Adding the mechanism and a stopgap that complement this thread.
Deletion is keyed on file mtime, not the transcript's own last-activity timestamp.
cleanupOldSessionFilesinsrc/utils/cleanup.tswalks~/.claude/projects/<slug>/and callsunlinkIfOld, which deletes any*.jsonl/*.castwithstat.mtime < now - cleanupPeriodDays. Because mtime is externally mutable, anything that touches it flips the outcome: a restore (cp,tar -x,rsyncwithout-a), a sync client, or a script that sets mtimes to a session's true (old) last-activity date makes a present session look old, and it is silently deleted on the next sweep. (I hit exactly this: normalizing session mtimes for picker chronology made ~30-day-old sessions eligible and deleted 11.) Keying retention on the last in-file messagetimestampinstead of mtime would close this whole class of accidental loss.cleanupPeriodDays: 0is an overloaded footgun.getCutoffDate()with0returns now (deletes everything), andshouldSkipPersistence()treats=== 0as "don't persist". So the value that intuitively reads as "off" instead wipes all transcripts and stops writing new ones.The large-value workaround is not reliable (see #41458 / #45735): processes started with
--setting-sources local, or the SDK'ssettingSources: [](including autonomously spawned subagents), run cleanup that ignorescleanupPeriodDaysfrom~/.claude/settings.jsonand fall back to the default 30. SocleanupPeriodDays: 999999does not protect anyone who runs subagents or SDK sessions.A
SessionStartbackup hook is a nice stopgap until this is fixed: it copies transcripts out of the deletion path, independent of the buggy setting. The on-disk JSONL is append-only (/rewindorphans a dead branch and keeps appending; it does not shrink the file), so one high-water-mark backup per file is lossless: copy only when the backup is missing or the live file has grown. Keying on grew-only (not mtime, not size-differs) makes it both mtime-immune and shrink-safe.~/.claude/hooks/backup-sessions.sh:~/.claude/settings.json:Even if cleanup deletes a transcript, the latest state survives outside
~/.claude/projects/.Fix directions echoing this thread: retain by the in-file last-activity timestamp (not mtime); archive or trash instead of
unlink; make it opt-in or use a much longer default; and make the restricted-setting-source paths honorcleanupPeriodDays.---
Already lost sessions to this (on ext4)? An after-the-fact recovery toolkit - volatility-ordered: stop writes, dump live
claude --resumeprocess memory + webview state, take backup/ext4-journal wins, then raw-disk carve + journal-extent recovery - is at ojura/claude-skills (recover-deleted-sessions-ext4). It's triage, not a fix; theSessionStartbackup hook above is what keeps you from needing it.@ojura — thanks for the mtime/cleanup analysis above, it's the most informative comment I've seen so far. I've updated my repo's notes to credit it (and walked back some over-confident framing as a result): https://github.com/garrettmoss/restore-claude-history/blob/master/NOTES.md#why-updates-seem-to-trigger-this
Your
SessionStarthook is the right shape for forward-looking backup, which is the gap my Time Machine recovery tool doesn't fill (yet). I'd like to use it as the reference implementation for abackup_claude_history.pyalongside the restore script — credit you, link this comment, then extend with a CLI, restore-from-backup verb, retention policy, etc. Want to be involved (co-author, reviewer, anything), or happy for me to just run with it and credit?Also noticed your repo
claude-patches— that's a separate impressive rabbit hole. Likely not too much overlap with what I'm doing but good to know it exists.@garrettmoss sure, I will have my Claude talk to your Claude 😁
Adding a data point on scope, because this one hurt.
On macOS,
cleanupPeriodDayswas never surfaced to me — I found out only when/resumecame up nearly empty. On disk: 773 session folders, but only 132 surviving.jsonltranscripts. Roughly 640 conversations spanning January through mid-May were silently deleted, each leaving an orphanedsubagents//tool-results/folder behind. Like the OP, my observed retention was far shorter than the documented 30 days — survivors only go back about a week.No warning, no soft-delete, no recovery path: no Trash, and any backup or snapshot I had postdated the deletion. The only thing I could partially salvage was the prompts I'd typed (from
history.jsonl) — none of Claude's actual work.Months of context, gone with zero notice. Please make this opt-in, or at minimum warn before deleting and soft-delete to Trash rather than unlinking. Thanks for tracking this.
@KirkPX have a look at the .jsonl carving skill in ojura/claude-skills, it might save some chats.
Adding a Windows Desktop data point.
Lost two sessions in the same project (
Billy AI FishandBilly AI Fish 2, 44 and 52 days old) to today's startup cleanup on Claude Code Desktop 2.1.156 / Windows 11. Both were explicitly maintained as long-running reference threads — the verbatim chat was active continuation context for ongoing hardware/software work on a Raspberry Pi side-project, not idle scratch.After the cleanup, the sidebar shows "Sitzung nicht auf dem Datenträger gefunden" ghost entries (same symptom as #62959). The
.jsonlfiles in~/.claude/projects/<project>/and the corresponding%APPDATA%\Claude\local-agent-mode-sessions\<workspace>\<project>\local_<sid>\.claude\projects\…paths were already unlinked, with no Recycle Bin entry (cleanup usesfs.unlink).Two concrete points for the fix discussion:
cleanupPeriodDays: 0is rejected by schema validation with a message recommending a large number instead (workaround:36500). That validation error is currently the only place the workaround is documented — discoverable only after data loss. Accepting0as the intuitive "never clean up" value would close a real UX gap at effectively zero cost.MEMORY.md/project_*.mdfiles under~/.claude/projects/<project>/memory/survived and made partial reconstruction possible — genuinely good design. But a curated summary isn't a substitute for the verbatim flow that captures why particular decisions were made; you can rebuild the what from memory files, not the reasoning behind tried-and-rejected approaches.Strong +1 to soft-delete to the OS Recycle Bin, UI exposure of
cleanupPeriodDays, and a pre-cleanup warning with per-session pin.I need this fixed. Claude is literally destroying work information I have generated with my tokens. This is an unacceptable state.
Thanks @ojura — your
unlinkIfOld/ mtime analysis (and the--setting-sources local/settingSources: []override that defeats a highcleanupPeriodDays) is the clearest root-cause account on this bug — and @garrettmoss for the open-source recovery script.A couple of data points and one ask, observed on
claude-opus-4-8/ Claude Code, 2026-05-30 (default config, nocleanupPeriodDaysset):~/.claude/projects/so cleanup can't reach it. (Without a prior backup it's filesystem carving, per @ojura / @garrettmoss — far harder.)For breadth, the documentation gaps are tracked separately from this bug: #51779, #38576, #63777 (cleanup-scope omissions) and #63842 (session pinning).
One ask of Anthropic: before any delete, prompt for consent or move files to the OS trash/Recycle Bin (or a
~/.claude/trash/grace area) — and makecleanupPeriodDayshonoured by all setting sources, including SDK-spawned subagents (the gap @ojura identified). An optional export-on-cleanup (a compact JSONL/MD snapshot at deletion time) would make even an expired session recoverable.I was surprised by this today - I've only been using Claude Code for a bit over a month, luckily I made backups (for the purposes of using cross coding agent search) but this should definitely be surfaced without having to do a Q&A with Claude.
and when I pressed further...
For users wanting an independent recovery layer before this is fixed: Clean My Agent (I'm the author) backs up and exports session JSONLs before any cleanup pass runs.
Corroborating with an independent case, plus a primary-source timeline of how this behavior and its disclosure evolved (claude-code
CHANGELOG.md, npm publish timestamps, and Wayback Machine snapshots of the docs).Another independent data-loss case
cleanupPeriodDayskey in any settings file — binary default in effect.~/.claude/.last-cleanupconfirms the sweep runs at session start, consistent with this issue's observation (it even ran mid-investigation when a stray CLI invocation spawned a session).~/.claude/plans/that a surviving 2026-06-09 transcript references as existing was gone by 2026-06-12, while a newer plan file (mtime 2026-05-30) survives — consistent with an age-based sweep of~/.claude/plans/that I cannot find in any changelog entry or documentation. Maintainers: please enumerate the complete set of paths the retention sweep touches.Transcripts are load-bearing data — by the product's own design
The strongest argument that silent deletion is wrong here isn't any individual workflow; it's that Claude Code itself treats transcripts as durable data and builds features on top of them:
--resume,--continue, and/forkare transcript-backed — the resume picker is an implicit promise that history persists.A system that encourages building a knowledge substrate on these files, then deletes that substrate on a rolling 30-day fuse without telling anyone, is internally contradictory. Transcripts are not cache; the artifact records what was decided, the transcript is frequently the only record of why.
Verified timeline (CHANGELOG.md + npm publish dates + Wayback)
| Date | Version | Event |
|---|---|---|
| 2025-05-17 | v0.2.117 | Changelog: "Introduced settings.cleanupPeriodDays" — the deletion behavior ships |
| 2025-06-15 | — | Wayback,
docs.anthropic.com/en/docs/claude-code/data-usage: sole disclosure is "Users' local Claude Code clients may store sessions locally for up to 30 days so that users can resume them. This behavior is configurable." — framed as a privacy reassurance ("up to 30 days"), not a data-loss warning; setting not named || 2025-09-03 | — | Wayback: "Local caching" bullet appears on the same page; still "(configurable)" with no setting name |
| 2026-03-24 | v2.1.83 | "Fixed tool result files never being cleaned up, ignoring the
cleanupPeriodDayssetting" — sweep scope grows || 2026-03-31 | v2.1.89 | "
cleanupPeriodDays: 0… rejected with a validation error — it previously silently disabled transcript persistence" — the only "keep forever"-adjacent value removed; minimum is now 1, retention-forever requires an arbitrarily large number || 2026-04-10 | v2.1.101 | "Fixed
--setting-sourceswithoutusercausing background cleanup to ignorecleanupPeriodDaysand delete conversation history older than 30 days" — a confirmed fail-destructive path: when settings resolution fails, cleanup falls back to the 30-day default and deletes. This corroborates #41458 (explicit99999setting, 490 sessions deleted anyway) || 2026-04-15 | v2.1.110 | "Fixed session cleanup not removing the full session directory including subagent transcripts" — confirms the orphaned
subagents/dirs documented in this issue were the sweep's doing, and the fix was to delete more, not to warn || 2026-04-21 | v2.1.117 | "The
cleanupPeriodDaysretention sweep now also covers~/.claude/tasks/,~/.claude/shell-snapshots/, and~/.claude/backups/" — a directory named backups is now age-swept || 2026-04-30 | v2.1.126 |
claude project purgeadded with--dry-run,-i,-y— the manual deletion path gains exactly the safety affordances the automatic path still lacks || Apr 1 – May 6, 2026 | — | Wayback bisection of
code.claude.com/docs/en/data-usage: the namecleanupPeriodDaysfirst appears on the user-facing data page in this window — ~11 months after the behavior shipped, and only after #41458/#46621 were filed |Two structural points fall out of this timeline:
/en/data-usageis about Anthropic's server-side data handling; client-side destruction of local files is buried inside it, originally phrased as a privacy guarantee ("up to 30 days"). A user asking "will Claude Code delete my files?" would never look there — and for the first year, the page didn't name the setting that controls it.On the suggested fixes
Everything in this issue's list, with two points sharpened:
--setting-sourcesconfigurations, subprocess environment), cleanup must be skipped, not defaulted to 30 days. A?? 30fallback in a deletion path converts every settings-resolution bug into permanent data loss.unlink; a cleanup log of what was deleted when.Workaround status (for others landing here)
is schema-valid and is honored on 2.1.175 — verified empirically: a transcript aged exactly 30 days survived a cleanup pass that ran after the setting was added. But per #41458 and the v2.1.101 changelog entry, a large value is not a guarantee — any settings-resolution failure reverts to fail-destructive defaults. The only real protection today is external backups of
~/.claude/projects/.---
Transparency note: this comment was researched and drafted by Claude Code itself (Claude Fable 5), at my direction and with my review — the changelog mining, npm timestamp mapping, and Wayback bisection above were agent-performed. The loss was discovered the same way: the agent went looking for artifacts from its own past sessions and found the transcripts already deleted.
This bug is non-sense. Please either (1) make auto deletion opt-in with "keep forever" as the default, (2) prompt the user before deleting anything, or (3) show a clear warning in settings. Real failure case: take parental or medical leave for more than 30 days, come back, everything is gone.
+1, and I want to underline the "no warning" part. I lost 201 sessions to cleanupPeriodDays with zero indication that a deletion was pending or had already happened. I only found out when I went looking for an old session and it was just gone.
A couple of concrete follow-ups from my own run-in with this:
1. A minimal warning would have prevented my entire loss. It does not need to be fancy. Even a startup line like:
with a daily reminder until deletion would be enough. The issue is not that cleanup exists, it is that it is silent and irreversible. A 7-day heads-up with a one-key escape hatch turns a data-loss event into a non-event. Alternatively, users could "pin" certain sessions to mark them safe from deletion.
2. The mtime keying punishes your most valuable sessions. Cleanup keys on file mtime, so the sessions most likely to get deleted are the long-lived, high-context ones that have been sitting on the back burner. Not touched recently, but definitely not meant to disappear. "Dormant" is being treated as "disposable," and those are often opposites (research threads, long-lived reference and analysis work, architecture discussions you come back to once a month).
3. Partial-recovery workaround if you already got hit: your typed prompts survive even after the transcript is gone, because
~/.claude/history.jsonlis not part of the cleanup sweep. Group its entries bysessionIdand you can rebuild your half of every deleted conversation. The assistant replies are gone, but the prompts bring back a surprising amount of context. I used this to salvage 201 deleted sessions. It is a workaround, not a fix, and it should not be necessary.Agree with @ProIcons on the core point:
These are the user's files on the user's machine. Whether and when they get deleted should be the user's call, not a silent default. Automatic tidying is fine as a feature. It is not fine as a default, and it really should not be the fallback when settings fail to resolve (the fail-destructive path in the v2.1.101 changelog shows that is a real code path, not a hypothetical).
Getting consent is cheap. Prompt once on first startup after the feature lands (which also covers upgrades of existing installs), or at the very least right before the first pass that would delete anything. Give two clear choices ("keep everything" or "clean up after N days") and delete nothing until the user picks one. Warnings, previews, and soft-delete all help, but they are patches on a default that should not exist. Ask first and this whole category of "my sessions just vanished" reports goes away.
Just got hit by this in #62476 . I'm flabbergasted this is still unfixed after 2 months.
How is deleting the user's conversation history without their consent okay??
Another data point: lost a 239-prompt session spanning 3+ weeks of work (May 11–Jun 5) on macOS — returned to resume it on day 34 and the transcript had been silently purged. Nothing in Trash, no archive, no recovery; only my prompts survived via history.jsonl. Also filed #76173 with concrete proposals (soft-delete to archive > startup warning + confirm > minimum: deletion log + a hint in the resume-failure message pointing at cleanupPeriodDays).