[plugin] installed_plugins.json accumulates stale installPath entries; PATH wired to ghost dirs

Resolved 💬 1 comment Opened Apr 30, 2026 by oletizi Closed May 1, 2026

Symptom

~/.claude/plugins/installed_plugins.json accumulates entries pointing at cache directories that no longer exist on disk. Claude Code wires PATH from these stale entries on session start, so command -v <plugin-bin> returns empty even when:

  • The marketplace clone at ~/.claude/plugins/marketplaces/<marketplace>/plugins/<plugin>/bin/<bin> is intact and runs cleanly.
  • /plugin install <plugin>@<marketplace> reports success.
  • /plugin marketplace update <marketplace> reports success.
  • /reload-plugins reports the expected number of plugins loaded.

The user-facing failure is "I just installed the plugin and command -v <bin> returns nothing." There is no diagnostic surface that reveals the cache vs registry mismatch.

Concrete reproduction (deskwork plugin marketplace)

State on a development machine after several install / upgrade / reload cycles:

$ ls ~/.claude/plugins/cache/deskwork/
deskwork-studio                 # only directory present

$ node -e 'const d = JSON.parse(require("fs").readFileSync(process.env.HOME + "/.claude/plugins/installed_plugins.json", "utf8")); for (const [k, vs] of Object.entries(d.plugins)) { if (!/deskwork/.test(k)) continue; for (const v of vs) console.log(k, "version=" + v.version, "exists=" + require("fs").existsSync(v.installPath)); }'

deskwork@deskwork version=0.9.7 exists=false
deskwork@deskwork version=0.0.1 exists=false
deskwork@deskwork version=0.7.2 exists=false
deskwork@deskwork version=0.9.5 exists=false
deskwork@deskwork version=0.9.7 exists=false
deskwork@deskwork version=0.9.7 exists=false
deskwork-studio@deskwork version=0.7.2 exists=true
deskwork-studio@deskwork version=0.9.5 exists=false
deskwork-studio@deskwork version=0.9.7 exists=false
deskwork-studio@deskwork version=0.9.7 exists=false
dw-lifecycle@deskwork version=0.9.7 exists=false

$ command -v deskwork dw-lifecycle deskwork-studio
/Users/orion/.claude/plugins/cache/deskwork/deskwork-studio/0.7.2/bin/deskwork-studio    # only one resolves

$ echo $PATH | tr : '\n' | grep deskwork
/Users/orion/.claude/plugins/cache/deskwork/deskwork/0.9.4/bin       # ghost path — directory doesn't exist
/Users/orion/.claude/plugins/cache/deskwork/deskwork-studio/0.7.2/bin

11 registered entries, 1 valid path on disk. PATH includes a ghost entry (deskwork/0.9.4/bin) for a version that hasn't existed on disk for several releases.

Why this matters

This blocks adopters from using the plugin. The plugin's documented install / usage path produces a state where the bin is unreachable; the user has no in-band recovery. The current workaround documented downstream is "edit installed_plugins.json by hand, remove the bad entries, then reinstall." That is brittle and not user-friendly.

The bug appears to scale with usage — entries accumulate on every install / reinstall / upgrade cycle. A user who has been actively iterating on a plugin (across version bumps, marketplace updates, plugin enable/disable cycles) is more likely to land in the broken state than a fresh adopter.

Expected behavior

  • On /plugin install, prune any prior entries for the same <plugin>@<marketplace> key whose installPath doesn't exist on disk before adding the new entry.
  • On /reload-plugins, reconcile the registry against the cache directories. Stale entries (registered but missing on disk) should either be re-materialized from the marketplace clone or pruned.
  • On session start, before wiring PATH, verify each registered installPath exists. If not, either skip it or warn the user that the plugin is in an inconsistent state.

Suggested minimal fix

The PATH-wire on session start is the highest-leverage point. A check that filters installPath entries against fs.existsSync before appending them to PATH would prevent the ghost-path symptom alone. The accumulation problem (entries never being pruned) can be addressed separately.

Downstream mitigation

The deskwork project shipped a deskwork repair-install subcommand (v0.9.8) that prunes stale entries for *@deskwork keys. It runs portably via the marketplace-clone bin path so adopters with broken PATH can self-heal:

~/.claude/plugins/marketplaces/deskwork/plugins/deskwork/bin/deskwork repair-install

Each plugin marketplace would have to ship its own equivalent for full coverage. A first-party fix in Claude Code is the cleaner path.

Environment

  • Claude Code: 2.1.121 (AI_AGENT=claude-code/2.1.121/agent)
  • macOS: Darwin 24.6.0
  • Plugin marketplace: audiocontrol-org/deskwork (3 plugins)
  • All three plugins were installed via the canonical /plugin marketplace add + /plugin install flow at various points.

Related downstream issue

audiocontrol-org/deskwork#89 — full timeline of the dogfood discoveries that motivated this report, including the v0.9.5+ fresh-install cases that contradict the original "no release blocker" framing.

View original on GitHub ↗

This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗