[BUG] Misleading `plugin-cache-miss` ("not cached at <existing path> — run /plugin to refresh") when the real cause is a missing install record for the current project
Preflight Checklist
- [x] I have searched existing issues and this hasn't been reported yet
- [x] This is a single bug report (please file separate reports for different bugs)
- [x] I am using the latest version of Claude Code (2.1.220)
What's Wrong?
When a marketplace plugin is enabled in a project's checked-in .claude/settings.json but has no install record for that project in installed_plugins.json (e.g. it was installed with --scope project from a different repo), the /plugin Errors panel reports:
Plugin "workflow" not cached at /Users/me/.cache/claude/plugins/cache/bitwise/workflow/1.0.0
Run /plugin to refresh the plugin cache
Both halves of that message are wrong:
- The path exists and is fully populated — including
.claude-plugin/plugin.json. Nothing is missing from the cache. /pluginrefresh does not fix it, because the cache was never the problem. The plugin simply has no install record whose scope matches the current project.
The accurate error type for this situation already exists in the codebase — plugin-not-installed, which renders as "Plugin "X" is enabled in project settings but isn't installed — run claude plugin install X@marketplace --scope project" and points at the actual fix — but it is unreachable for the most common kind of plugin (see analysis below).
Users (and coding agents) reading the current message chase phantom cache corruption. In my case the message survived multiple "refresh" attempts across days before I traced it to the record scoping.
Analysis (from the minified 2.1.220 bundle)
In the cache-only plugin loader, for a plugin whose marketplace entry has a string source (a marketplace-relative path like "./plugins/workflow", i.e. the standard marketplace layout):
- The install record is looked up through a project filter (record matches if
scopeisuser/managed, orprojectPath === cwd). When no record matches, the resolvedinstallPath(a) isundefined. - The string-source branch then immediately pushes the error — without any existence check ever failing:
if (!u && a && (a.endsWith(".zip") ? await e_(a) : await KVe(a))) c = a;
else if (!u) {
if (o) i.push({ type: "plugin-cache-miss", source: n, plugin: e.name,
installPath: a ?? l ?? t }); // <-- a is undefined here
return null;
}
lis the fallbackT.plugins[b]?.find((N) => N.installPath)?.installPath— i.e. any record's install path, including one scoped to a different project. So the error prints a path that exists and validates fine, which is what makes the message so misleading.- The guard that produces the accurate
plugin-not-installederror is gated ontypeof x.entry.source !== "string", so it can never fire for string-source marketplace plugins — the exact plugins the checked-in team-onboarding flow (extraKnownMarketplaces+enabledPlugins) produces.
Steps to Reproduce
- Repo A and repo B, both with a checked-in
.claude/settings.jsondeclaring the same git marketplace inextraKnownMarketplacesand"my-plugin@my-marketplace": trueinenabledPlugins. The marketplace'smarketplace.jsonuses relative-path plugin sources ("source": "./plugins/my-plugin"). - In repo A: install the plugin (
claude plugin install my-plugin@my-marketplace --scope project). It loads fine there. - Open Claude Code in repo B and check
/plugin→ Errors.
Actual: Plugin "my-plugin" not cached at <repo A's installPath> — Run /plugin to refresh the plugin cache, where the path exists and is fully populated. Refreshing does not resolve it.
Expected: the existing plugin-not-installed error — "Plugin "my-plugin" is enabled in project settings but isn't installed — run claude plugin install my-plugin@my-marketplace --scope project" — which names the actual cause and the actual fix.
Workaround
claude plugin install my-plugin@my-marketplace --scope user — a user-scope record passes the project filter everywhere, and per-repo enabledPlugins keeps working as the enable/disable switch.
Related issues
The underlying single-record / project-scope limitations are tracked in #73796 (record projectPath ping-pong across repos) and #81706 (dual-scope install collapsing to a project record). This report is specifically about the diagnostics: plugin-cache-miss being emitted with another project's (existing) path, and its "run /plugin to refresh" hint, when the real condition is "no install record for this project". Fixing the message would make those underlying issues self-diagnosing instead of looking like cache corruption.
Environment
- Claude Code 2.1.220 (Homebrew cask
claude-code@latest) - macOS (Darwin 27.0.0, arm64)
- Marketplace: private git repo added via
extraKnownMarketplaces, relative-path plugin sources
3 Comments
Reproduced on 2.1.233. Setup mirroring the report: a git marketplace whose
marketplace.jsonuses relative-path plugin sources, plugin installed with--scope projectin repo A, and the plugin enabled via repo B's checked-in.claude/settings.json.In repo B the plugin fails to load and the Errors panel / debug log show exactly the reported message:
while that directory exists and is fully populated (including
.claude-plugin/plugin.json), and refreshing doesn't help. The debug log even records that the plugin was recognized as "enabled only by repo-authored settings" without an install record for the current project — so the underlying condition is exactly what you diagnosed, and the actionable "isn't installed — runclaude plugin install …" message that exists for other plugin types never fires for standard relative-path marketplace plugins.One extra data point from testing: with a local directory marketplace the plugin loads fine in repo B (it's read in place from the marketplace source), so this misleading diagnostic is specific to remote (git/GitHub/URL) marketplaces — which matches your setup.
Confirming this as a diagnostics bug: the error should say the plugin has no install record for the current project and point at
claude plugin install <plugin>@<marketplace> --scope project(or a user-scope install), rather than reporting a cache miss at a path that exists. Thanks for the precise analysis — it checks out end to end.🤖 Generated with Claude Code
Same issue here. I wish this is fixed soon, this is really annoying!
Still reproduces on 2.1.291 (macOS, git marketplace with
./plugins/<name>sources). Same message, cache directory fully populated,/pluginrefresh does nothing.The misleading text cost real time here too: the first thing checked was the cache, then
claude plugin validate, then the hook scripts, all clean. In one of our repos the actual cause was that the plugin's only install record had aprojectPathpointing at the repo's old location, before it was moved. A message naming the condition (no install record for this project, with the path it looked for) would have pointed straight at that.