Plugin cache: session-start .in_use PID locks are never reaped (285k accumulated), starving superseded-version cleanup

Resolved 💬 0 comments Opened Jun 4, 2026 by bradfeld Closed Jun 5, 2026

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_use dirs 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_use before 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/cache session-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.

View original on GitHub ↗