[Bug or Doc] Session-start plugin reconciliation silently deletes cache subdirectories not in current project's enabledPlugins
Summary
Claude Code's session-start plugin reconciler silently deletes cache subdirectories under ~/.claude/plugins/cache/<marketplace>/ whose plugin id is not in the current project's .claude/settings.json enabledPlugins map. In multi-project setups where projects share a marketplace but enable different subsets of plugins, this produces cache-cycling on every cross-project session boot.
Filing for clarification + documentation: unclear if this is intentional design (just undocumented) or a bug.
Environment
- Claude Code 2.1.119
- macOS Tahoe (Darwin 25.3.0)
- Multi-project setup: Project A enables full plugin set; Project B enables a subset; both reference the same marketplace.
Reproduction
Setup: A marketplace with multiple plugins (e.g., kernel-plugin + feature-plugin-A + feature-plugin-B + feature-plugin-C).
Project A .claude/settings.json enabledPlugins: all four plugins enabled.
Project B .claude/settings.json enabledPlugins: only kernel-plugin + feature-plugin-A.
After installing all four (e.g., from Project A), the cache directory contains:
~/.claude/plugins/cache/my-marketplace/
├── kernel-plugin/
├── feature-plugin-A/
├── feature-plugin-B/
└── feature-plugin-C/
Action: Open claude in Project B (which has the smaller enabledPlugins set). Immediately /exit.
Observed: feature-plugin-B/ and feature-plugin-C/ cache subdirectories are silently deleted at session start by the claude process tree. Captured via fswatch -x -t -r ~/.claude/plugins/cache/<marketplace>/:
Sun Apr 26 11:34:21 2026 .../feature-plugin-B/0.1.0 IsSymLink Removed
Sun Apr 26 11:34:21 2026 .../feature-plugin-B IsDir Removed
Sun Apr 26 11:34:21 2026 .../feature-plugin-C/0.1.0 IsSymLink Removed
Sun Apr 26 11:34:21 2026 .../feature-plugin-C IsDir Removed
[and 13 more for sibling plugins not in Project B's enabledPlugins]
All deletions captured at single second 11:34:21 during session boot. No /plugin uninstall issued, no manual deletion.
Reopening Project A then re-installs/re-symlinks the deleted plugins (16 Created events captured at next session boot).
Expected behavior
Choose one — this is the clarification question:
- Cache should persist across project boundaries. Per-project enablement should toggle which plugins LOAD, not delete cache files. Switching projects should not require re-fetching plugins, especially given the marketplace cache appears intended as a global shared store.
- Current behavior is intentional but should be documented. The Plugins reference doesn't explicitly cover this lifecycle. A "Plugin scopes and cache lifecycle" section explaining when cache subdirectories get deleted (and why) would set correct expectations.
Impact
- Performance: every cross-project session boot triggers cache rebuild for non-overlapping plugin sets. For large plugin trees with binary assets this can be significant.
- Symlink dev-loops break: developers using
ln -s <source> <cache-path>to iterate on plugin source lose their dev-loop on every cross-project session boot — the entire cache subdir gets removed (not just the symlink). - Operator confusion:
/pluginUI may show "installed" while the cache subdir is actively cycling between sessions. - Recovery-tooling proliferation: because the deletion is undocumented, downstream operators ship recovery layers (auto-restore hooks, manual reinstall scripts) without realizing the underlying mechanism is per-project gating.
Workaround that worked
Reinstalling kernel-style plugins (ones that should always be available regardless of project context) at scope: "user":
/plugin install <plugin>@<marketplace>
# At install prompt, select "Install for you (user scope)"
scope: "user" entries appear to bypass per-project enabledPlugins reconciliation — once one is registered, the cache subdir survives transitions between projects. Verified by soak-testing across multiple project boundaries with fswatch instrumentation: zero deletion events on user-scoped plugins.
This is workable but feels like a workaround rather than the intended path, especially for plugins that aren't truly user-global (e.g., a kernel/runtime that a small set of projects share).
Adjacent reports
- #52218 — different but related:
installed_plugins.jsonfield desync between reconciliation paths. - #15329 (CLOSED) — "Plugin cache prevents per-project installation when globally cached"
- #26513 (CLOSED) — "Plugin UI shows local-scoped plugin as 'installed' in unrelated projects"
- #29997 (CLOSED) — "Plugin installed at project scope cannot be reinstalled at user scope from marketplace"
These adjacent issues describe scope/visibility confusion. None explicitly call out the deletion behavior at session start.
Ask
If intentional → please add a "scope and cache lifecycle" section to the Plugins reference. The deletion-vs-persistence rule per scope (user / project / local) and the role of enabledPlugins in the deletion gate would resolve this for operators.
If unintentional → happy to provide more trace data (full fswatch + lsof + ps logs available locally; can share anonymized excerpts on request).
Either way, it would help operators avoid building recovery-layer toolchains for behavior that's actually expected.
This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗