[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

Status Open
Reported on v2.1.220
Maintainer reply ✓ Yes — bcherny
Activity 3 comments · opened Jul 28, 2026
💡 Likely answer: A maintainer (bcherny, collaborator) responded on this thread — see the highlighted reply below.

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:

  1. The path exists and is fully populated — including .claude-plugin/plugin.json. Nothing is missing from the cache.
  2. /plugin refresh 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 scope is user/managed, or projectPath === cwd). When no record matches, the resolved installPath (a) is undefined.
  • 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;
}
  • l is the fallback T.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-installed error is gated on typeof 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

  1. Repo A and repo B, both with a checked-in .claude/settings.json declaring the same git marketplace in extraKnownMarketplaces and "my-plugin@my-marketplace": true in enabledPlugins. The marketplace's marketplace.json uses relative-path plugin sources ("source": "./plugins/my-plugin").
  2. In repo A: install the plugin (claude plugin install my-plugin@my-marketplace --scope project). It loads fine there.
  3. 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

View original on GitHub ↗

3 Comments

bcherny collaborator · 1 month ago

Reproduced on 2.1.233. Setup mirroring the report: a git marketplace whose marketplace.json uses relative-path plugin sources, plugin installed with --scope project in 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:

Plugin "my-plugin" not cached at ~/.claude/plugins/cache/my-marketplace/my-plugin/1.0.0 — run /plugin to refresh

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 — run claude 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

toverux · 1 month ago

Same issue here. I wish this is fixed soon, this is really annoying!

flound1129 · 9 days ago

Still reproduces on 2.1.291 (macOS, git marketplace with ./plugins/<name> sources). Same message, cache directory fully populated, /plugin refresh 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 a projectPath pointing 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.