[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
This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗