Plugin updater leaves stale version directories in cache; loader globs all of them
Plugin updater leaves stale version directories in cache; loader globs all of them
Summary
When Claude Code updates an installed plugin from version X to version Y, the new version's source is cloned to ~/.claude/plugins/cache/<marketplace>/<plugin>/<Y>/ and installed_plugins.json is updated to point there. The old <X>/ directory is never deleted. The plugin loader then globs every version present in the cache, registering duplicate skills, MCP tool names, and (for plugins with MCP server instructions) duplicate instruction blocks in the SessionStart system prompt.
Net effect: silent token bloat on every session that grows with each plugin update.
Reproduction
- Install a plugin that ships skills (e.g.
vercel@claude-plugins-official). - Wait for or force a version bump.
- After the update, look at
~/.claude/plugins/cache/claude-plugins-official/vercel/— both old and new version directories are present. - Start a new Claude Code session and observe the available-skills listing in the SessionStart system reminder. Skills like
vercel:ai-sdkappear multiple times, once per cached version.
Observed scope (real environment)
$ ls ~/.claude/plugins/cache/claude-plugins-official/vercel/
0.40.0 0.40.1 0.42.1 ← installed_plugins.json points only at 0.42.1
$ ls ~/.claude/plugins/cache/interagency-marketplace/interflux/
0.2.67 0.2.68 0.2.69 ← only 0.2.69 is installed
$ ls ~/.claude/plugins/cache/interagency-marketplace/interlock/
0.2.12 0.2.13 0.2.14 ← only 0.2.14 is installed
$ ls ~/.claude/plugins/cache/interagency-marketplace/tldr-swinton/
0.7.17 0.7.18 0.7.19 ← only 0.7.19 is installed
installed_plugins.json correctly shows a single active version per plugin via installPath. The loader is not consulting it.
Token impact (measured)
After cleaning out the stale versions for a user with 60+ enabled plugins, the per-session skill-listing dropped by ~6,300 tokens (139 skills, ~28k chars → previously 266 skills, ~53k chars). Most of that was duplicate descriptions for plugins that had been updated multiple times.
For long-running sessions or users with auto-update enabled, this leak compounds: every plugin update adds another full copy of that plugin's skills/tools/instructions to the session prompt indefinitely.
Two related bugs
- Updater leak: Plugin update flow writes new version dir, atomically switches
installed_plugins.json, never removes prior version dir.
- Loader fan-out: Plugin loader appears to discover plugins by scanning cache directories rather than by reading
installed_plugins.json. Even if the updater were fixed, a plugin loader that loads only the version listed ininstalled_plugins.jsonwould be defense-in-depth — and would also handle the case where users have manually added old versions.
Suggested fixes
- Updater: After successful version switch,
rm -rfthe previousinstallPath. The path is already known frominstalled_plugins.jsonbefore the swap. - Loader: Iterate
installed_plugins.jsonentries and load each entry'sinstallPathdirectly. Don't globcache/<marketplace>/<plugin>/*. - Defense: At session start, log a warning when cache contains version dirs not referenced by
installed_plugins.json.
Workaround
Users can run ic publish clean (third-party tool) or manually rm -rf stale version dirs. This shouldn't be necessary.
This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗