Plugin cache: session-start .in_use PID locks are never reaped (285k accumulated), starving superseded-version cleanup
Summary
Every Claude Code session start writes a {"pid":N,"procStart":"..."} lock file named after the session PID into every enabled plugin's .in_use/ directory under ~/.claude/plugins/cache/<marketplace>/<plugin>/<version>/.in_use/. Nothing deletes the lock on session exit, and no janitor sweeps dead-PID entries. On a machine with heavy session turnover this accumulates without bound, and the stale locks prevent the plugin updater from ever reaping superseded plugin version directories.
Observed
- 285,525 dead-PID lock files across 17
.in_usedirs at first measurement (2026-06-03), with 2,940 live-PID entries kept - After a full manual sweep, ~6,800 new locks re-accumulated within ~24 hours (~1,000 session starts/day × ~9 enabled plugins on this machine — 12 git worktrees, headless scheduled runs, frequent fresh sessions)
- Superseded plugin version dirs piled up alongside (e.g., release-radar 1.0.0–1.2.0, simmer 3.0.0, documentation-audit 2.0.0, episodic-memory 1.4.1, vercel-plugin 0.40.0, vtsls 0.1.0) because the updater checks
.in_usebefore reaping and sees thousands of stale "live" references - One-shot CLI invocations (
claude -p ...) do not write locks — verified; it is interactive session starts only - A leftover
temp_git_*marketplace clone also persisted in the cache dir
Environment
- Claude Code 2.1.162, macOS (Darwin 25.5.0)
- Plugins from multiple marketplaces (superpowers, claude-code-lsps, vercel, etc.)
Expected
Either cleanup-on-exit for the session's own lock, or a dead-PID sweep before the updater's .in_use liveness check. The procStart field in the lock format suggests a PID-reuse-safe reaper was designed (compare stored proc start time against the live process) but it does not appear to engage for this path: locks for PIDs that have been dead for days remain on disk indefinitely.
Related
- #58881 reported the crash-leak case but was auto-closed as a duplicate of #57413, which is a Windows zombie-process hang — a mismatched dedup
- #65147 covered the remote cache (
~/.claude/remote/plugins) and was retracted by its author after finding liveness-aware reaping exists in the CLI; whatever reaper exists, it demonstrably does not fire for the local~/.claude/plugins/cachesession-start locks at this volume
Workaround
We now run a daily external janitor (build live-PID set once via ps -axo pid=, comm -23 against each .in_use listing, delete dead-PID locks, then reap superseded version dirs not referenced by installed_plugins.json). Happy to share the script.