[BUG] Plugin manager never reclaims its staging clones — ~/.claude/plugins/cache/temp_* grows without bound (1318 dirs / ~8 GB in 8 days)
What happened
The plugin manager leaves its staging clones behind in ~/.claude/plugins/cache/ after marketplace operations complete. They are never reclaimed, so they accumulate until the volume fills.
On this machine they grew to 1318 orphaned directories (~8.0 GB) over 8 days and contributed to a full-disk condition (0 bytes free on the data volume).
Where
~/.claude/plugins/cache/ — alongside the real, versioned installs:
~/.claude/plugins/cache/
├── claude-plugins-official/… ← real install (correct, served from here)
├── temp_git_1784761345113_n8wa6i/ ← orphan
├── temp_subdir_1784761342562_wgulhd.clone/ ← orphan
└── temp_github_…/ ← orphan
Three prefixes observed: temp_git_*, temp_subdir_*.clone, temp_github_*.
Naming pattern: temp_<kind>_<epoch_ms>_<rand6>.
They are full clones, not scratch files
Each orphan is a complete git working tree with .git and a valid origin:
$ ls -a ~/.claude/plugins/cache/temp_git_1784761345113_n8wa6i
. .. .editorconfig .git .github .gitignore .nvmrc …
$ git -C … remote get-url origin
https://github.com/obra/claude-session-driver.git
They are referenced by no plugin configuration — the installed plugins are served from the versioned <marketplace>/<version>/ directories, not from these.
It is a leak, not a one-time pile
Timestamps are embedded in the directory names, so the rate is directly measurable:
| Window | Orphans created |
|---|---|
| single 24 h window | 471 |
| peak hour (active use) | 48 |
| another active hour | 35 |
Seven temp_git_* clones were created within 10 milliseconds of each other
(…1345104, …1345106, …1345107, …1345109, …1345111, …1345113, …1345114),
which suggests a parallel fan-out over configured marketplaces (21 configured here).temp_subdir_* and temp_git_* appear in pairs ~20 ms apart, suggesting both survive the
same code path.
Rate works out to roughly 20–30 directories/hour during active use.
Expected
Staging directories are removed once the marketplace add/update/refresh they belong to
finishes — on the success path, not only on error paths. A stale-staging reclaim on
startup would also cover crashes and interrupted operations.
Actual
They persist indefinitely and accumulate without bound.
Possibly related
#77107 reports AV interrupting a plugin install mid-rename, leaving half-installed
state — same clone-then-rename area, but that issue is about the interrupted path while
this one reproduces on the successful path.
Environment
- Claude Code 2.1.218
- macOS (Apple Silicon, APFS)
- 21 marketplaces configured
Workaround
Deleting ~/.claude/plugins/cache/temp_* is safe once the operations that created them
have finished — they hold no installed-plugin state and every one is re-clonable from its
origin. Worth gating on age and on there being no open file handle, so an in-flight
operation is never touched:
find ~/.claude/plugins/cache -maxdepth 1 -name 'temp_*' -mmin +180
Note for anyone scripting this: the directory mtime is not a reliable age signal —
an interrupted rm -rf updates it, so a half-deleted carcass looks newly created and an
mtime-based filter will skip it forever. The epoch embedded in the directory name is the
immutable creation time and is the better signal.
3 Comments
Corroborating this independently — Claude Code 2.1.220, macOS 26.6 (Apple Silicon) — and adding two things not yet in this thread: a second leak location, a concrete race-condition trigger, and a more severe compounding failure mode.
Second leak location
Same orphaning pattern also happens under
~/.claude/plugins/marketplaces/temp_<epoch_ms>/(full marketplace clones), not just~/.claude/plugins/cache/temp_git_*/temp_subdir_*. On this machine right now:~/.claude/plugins/marketplaces/temp_*— 51 dirs, ~2.0 GB~/.claude/plugins/cache/temp_git_*— 26 dirs, ~36 MBSame clone-then-atomic-swap pattern, same "meant to self-delete on success, no reaper otherwise."
Root cause detail: concurrent racing processes, no lock
Reproduced under
FORCE_AUTOUPDATE_PLUGINS=1, which triggers a marketplace sync on every singleclaudeinvocation. With many concurrent headless/automation sessions running per hour against the same marketplace, processes race to update it — there's no lock. Whichever process loses the race, or is killed/SIGTERM'd mid-clone, leaves its temp clone behind with no cleanup-on-kill and no garbage collection, matching the leak reported above.More severe compounding failure:
.git/indexcorruption → phantom deletionsSome orphaned clones aren't just inert disk usage — they lose their
.git/indexfile entirely, most likely from being killed at exactly the wrong moment during git's index-write. Once.git/indexis gone,git statusin that directory reports every tracked file as deleted.On this machine, three such corrupted orphaned clones showed ~18,500–18,900 phantom "deleted" file entries each — ~56,000+ phantom changes total. This is what surfaced the whole investigation: it showed up in a dev dashboard as "10,000+ git changes," which took real digging to trace back to orphaned plugin-marketplace temp clones rather than an actual working-tree problem.
No native remedy
Confirmed no
claude pluginsubcommand addresses this: checkedclaude plugin --help,claude plugin marketplace update --help, andclaude doctor— nothing garbage-collects or locks these.Workaround applied locally
FORCE_AUTOUPDATE_PLUGINSto0insettings.json(stops the every-invocation sync that was driving the race).temp_*/temp_git_*dirs older than 24h as a stopgap.Filing this as a report for an upstream fix, not an urgent ask — flagging it here rather than as a separate issue since it's the same underlying gap (clone-then-atomic-swap with no lock and no cleanup-on-kill), just a second leak path plus a more severe compounding failure mode. Given the corruption angle turns "disk growth" into "silently wrong git state that can mislead tooling/dashboards," it may be worth broadening this issue's scope (or cross-linking) to cover: (1) a lock/mutex around marketplace update so concurrent
claudeprocesses don't race the same clone, and (2) cleanup-on-kill (e.g., clone into a per-process dir with a SIGTERM/SIGKILL trap, or a startup sweep removing anytemp_*/temp_git_*dir whose parent operation is no longer running) — not just an eventual reaper for the success path.Thanks for the detailed data — the embedded-epoch rate analysis and the mtime caveat for cleanup scripts were genuinely useful.
I tried to reproduce this on the current release (2.1.233, fresh config directory, git-backed marketplace): marketplace add, marketplace update, and plugin installs from git-subdirectory sources — on both the success and the failure paths — all removed their
temp_*staging directories once the operation finished; nothing accumulated in~/.claude/plugins/cache/.I also verified the startup reclaim you asked for in "Expected": I planted a stale
temp_git_<old-epoch>_*directory in the cache by hand, and it was swept automatically on the next session start. So upgrading to the latest release should both stop the leak and clean up the orphans that have already accumulated (your manualrmoftemp_*remains safe in the meantime, as you noted).This appears to have been fixed in a recent release. Closing — but if you still see
temp_*directories accumulating after upgrading to the latest version, please comment and we'll reopen and dig into the remaining path.🤖 Generated with Claude Code
This still happens on current releases. You asked for a comment if
temp_*directories keep accumulating, so here is the data.Versions: Claude Code 2.1.287 (CLI) and 2.1.289 (the Claude desktop app's bundled Claude Code). Linux (CachyOS, kernel 7.2), btrfs. Both versions ran on this machine since 2026-10-01.
1. The startup reclaim does not sweep old orphans.
Before cleanup,
~/.claude/plugins/cache/held 4,788 orphan dirs (7.1 GB): 3,121temp_git_*+ 1,667temp_subdir_*.clone. Most were from 2026-09-14 (2,840) and 2026-09-15 (1,833), which is 23–24 days old. They survived many session starts on 2.1.285–2.1.289. The fix comment says a staletemp_git_<old-epoch>_*dir is swept on the next session start. That did not happen here.2. New orphans still appear.
After 2.1.287 was installed (2026-10-01), 41 new orphans appeared over 8 days: about 3–10 per day. The newest is from today. Examples (epoch-ms names):
The clones come from git-backed marketplace sources, e.g.
qodo-ai/qodo-skills,greptileai/claude-plugin,obra/superpowers,coderabbitai/skills.3. Nothing references the orphans.
installed_plugins.jsonandknown_marketplaces.jsonhave 0 references totemp_. All 64installPathentries point to the real versioned cache dirs.lsofshowed no open handles in anytemp_*dir. After I trashed everytemp_*dir older than 1 day, all 64 plugins still load.Likely trigger (not proven): the 2026-09-14/15 burst matches many short-lived, concurrent Claude Code processes. On the same days, 66
~/.claude.json.tmp.<pid>.<hex>files were also left behind (52 of them 0 bytes). This matches a process exit or kill before the staging-dir cleanup runs, plus a startup sweep that does not run or does not match these dirs.Workaround in use: a weekly user timer moves
~/.claude/plugins/cache/temp_*dirs older than 7 days to the Trash.