Plugins: enabled-in-settings but never installed loads nothing silently; marketplace autoUpdate never updates existing installs

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

Summary

Committing enabledPlugins + extraKnownMarketplaces to a project's .claude/settings.json does not make the plugin load in that project: if installed_plugins.json has no install record for that project path, the plugin's skills and hooks are silently absent — no warning, no auto-install. Relatedly, marketplace autoUpdate: true refreshes the marketplace clone but never updates existing installs, so per-project installs stay pinned to their install-time version indefinitely with no lag signal.

Environment

  • Claude Code 2.1.212, macOS (darwin 25.6.0)
  • Private GitHub marketplace (extraKnownMarketplaces → github source), plugin installed per-project scope

Bug 1 — plugin enabled in project settings but never provisioned: silently absent

We share a process plugin across team repos by committing this to each repo's .claude/settings.json (the docs' team-sharing pattern):

{
  "enabledPlugins": { "lanoticia-process@lanoticia": true },
  "extraKnownMarketplaces": {
    "lanoticia": { "source": { "source": "github", "repo": "lanoticia/claude-plugin" } }
  }
}

On a new repo (.../excelenteawards), sessions started with none of the plugin's skills or hooks available — invoking its slash commands got "unknown skill", with no indication why. The cause: ~/.claude/plugins/installed_plugins.json carried install records for three sibling repos but none for this project path. The enable flag assumes an install record that nothing had created. Running /plugin install lanoticia-process@lanoticia + /reload-plugins fixed it (our installed_plugins.json shows the record appearing at that moment: installedAt: 2026-07-31T19:09:15Z), but the failure mode cost ~20 minutes of diagnosis, and every new machine/teammate cloning the repo will hit it again.

Expected: either (a) settings are declarative — an enabled plugin whose marketplace is declared right there in the same file gets installed on session start (possibly behind a consent prompt), or (b) at minimum a visible session-start warning: "plugin X@Y is enabled in .claude/settings.json but not installed for this project — run /plugin install X@Y".

Actual: the plugin is silently absent.

Bug 2 — marketplace autoUpdate refreshes the clone but never the installs

The marketplace entry has autoUpdate: true. The marketplace clone under ~/.claude/plugins/marketplaces/<name> does get refreshed (its lastUpdated advances). But existing per-project installs keep their install-time version forever:

marketplace clone lastUpdated: 2026-07-30 (carrying plugin v0.1.13)

installed_plugins.json, same day:
  /repo-a  version 0.1.11  (installedAt 07-21)
  /repo-b  version 0.1.11  (installedAt 07-24)
  /repo-c  version 0.1.13  (fresh install 07-30 — new installs DO get the latest)

The two older repos ran 0.1.11 for over a week while the clone carried 0.1.13 — hook fixes and new skills silently missing, nothing surfaced the lag. (Their records' lastUpdated even advanced on 07-28 without the version changing.)

Expected: autoUpdate: true updates installs (that's what the flag reads as), or, if it intentionally only refreshes the marketplace clone, some signal that installs lag the marketplace (session-start notice, /plugin badge).

Actual: installs pin to install-time version with no signal; the only way to notice is to diff installed_plugins.json against the clone by hand. We've shipped a SessionStart hook in the plugin itself that does this diff as a stopgap, but it inherently can't cover Bug 1 — in the enabled-but-never-installed state, no plugin hooks load at all, which is why this needs a fix in Claude Code.

Steps to reproduce (Bug 1)

  1. In repo A: /plugin marketplace add <marketplace> then /plugin install some-plugin@<marketplace> (project scope). Commit the resulting enabledPlugins + extraKnownMarketplaces settings.
  2. Clone repo A to a fresh path (or a second machine), or copy the same settings block into repo B.
  3. Start a session there: the plugin's skills/hooks don't load; nothing reports why.

Steps to reproduce (Bug 2)

  1. Install a marketplace plugin at version N in a project; set the marketplace autoUpdate: true.
  2. Publish version N+1 to the marketplace repo; wait for (or trigger) a marketplace refresh — the clone now carries N+1.
  3. Sessions in the project keep running version N indefinitely; installed_plugins.json keeps version: N; no lag signal anywhere.

View original on GitHub ↗

3 Comments

alisettar · 2 months ago

Independent reproduction of Bug 2, plus five controls that narrow down what it is not. Setup matches yours closely: private GitHub marketplace declared via project extraKnownMarketplaces, plugin installed at project scope in five repositories, "autoUpdate": true on the marketplace entry.

Claude Code 2.1.228, Windows (win32 10.0.26100).

Observed

The marketplace registry entry's lastUpdated advances during a session, but installed_plugins.json records never move. Publishing a new plugin version and moving the tracked ref changes nothing for existing installs; only an explicit claude plugin update <plugin>@<marketplace> --scope project, run once per project directory, moves them.

Controls

  1. Not a headless artifact. First measured with claude -p; re-measured in a normal interactive session left open 13.8 min (past the documented ≤10 min delay). Same result.
  2. Not cross-session interference. Closed every session, opened exactly one. Registry lastUpdated advanced during it, so the refresh ran and was not throttled — installs still did not move.
  3. autoUpdate is honored. It is present as "autoUpdate": true in known_marketplaces.json, so the project-level setting does reach the registry.
  4. Not authentication. A credential helper is configured; GIT_TERMINAL_PROMPT=0 git ls-remote <repo> <ref> returns without prompting, and a manual git fetch inside the marketplace clone succeeds and advances the remote-tracking ref.
  5. Not scope. I also installed the same plugin at user scope and left a 13-minute session open in an unrelated directory. The user-scope record did not auto-update either, so this is not specific to per-project installs.

Working control case on the same machine

This is the part that I think localizes it. In a single auto-update pass (timestamps within the same second):

| | official marketplace | our marketplace |
|---|---|---|
| structure | not a git clone (no HEAD, no fetch refspec) | git clone |
| registration | seed/managed | settings-sourced (extraKnownMarketplaces) |
| ref | none | pinned |
| installs updated | yes (2 plugins, user scope) | no (5 installs, project scope) |

Both registry entries were stamped in that pass; only the official marketplace's plugins actually moved. Since control 5 rules out scope, the asymmetry appears to follow the marketplace type / refresh path rather than the install scope.

Related: the source-side never refreshed either — the clone's refs/remotes/origin/<ref> did not move while lastUpdated was being stamped. That matches the cache-only path described in #83777.

Bug 1 — also reproduced

Independently hit the same silent absence: enabledPlugins + extraKnownMarketplaces committed to a repo's .claude/settings.json, no install record for that project path, and sessions ran with none of the plugin's rules, skills or hooks — no warning. On two separate machines. Worth adding: starting a session does not register a settings-sourced marketplace either; an explicit claude plugin marketplace add was required, so the "commit two files and you're done" pattern silently does nothing on its own.

Our mitigation for Bug 1 is a plugin-independent SessionStart hook committed in each repo (deliberately not in the plugin — a plugin cannot report its own absence). It checks whether the plugin cache directory exists and prints install instructions if not.

On "no lag signal"

We added one on our side, in case it is useful as a suggestion: our plugin's own SessionStart hook reads the installed plugin.json version, reads the version from the marketplace catalog copy on disk, and when the catalog is ahead it writes a stale=<installed>-<catalog> field to a local log and warns in-session. It is entirely local (no network, one file read) and stays silent when the versions agree. Something equivalent surfaced natively would have saved us roughly a day: every symptom of this bug is invisible, and the version field in a log was the only channel that ever reported it.

bcherny collaborator · 1 month ago

Triaged on the current release (v2.1.233, Linux), using a git-source marketplace declared in a project's .claude/settings.json plus enabledPlugins, in a freshly trusted folder with a clean config.

Bug 1 (enabled in settings but never installed): the headline symptom is fixed in current releases. With enabledPlugins + extraKnownMarketplaces committed and no prior install record, the marketplace was registered automatically and the plugin's skills loaded and ran — no manual /plugin install needed. However, the plugin's hooks still don't run in that state (a SessionStart hook never fired until we explicitly installed the plugin), and the only visible signal is an entry under /plugin → Errors whose message ("not cached … refresh the plugin cache") doesn't point at the actual remedy. The docs now state that enabling an external-source plugin in a project's settings doesn't install it for other users — but a session-start warning naming the not-installed plugin still doesn't exist.

Bug 2 (marketplace autoUpdate never updates installs): reproduced on v2.1.233. With "autoUpdate": true on a third-party marketplace and a newer plugin version published, sessions never refreshed the marketplace nor updated the project-scope install (it stayed pinned at the install-time version, with no lag signal). Running claude plugin update <plugin>@<marketplace> --scope project updated immediately, so only the automatic path is affected. This contradicts the current settings documentation, which says autoUpdate: true makes Claude Code "refresh that marketplace and update its installed plugins in the background after startup".

Keeping this open for the remaining gaps: hooks silently absent (with a misleading error) for settings-enabled-but-not-installed plugins, and autoUpdate not updating third-party marketplaces/installs.

🤖 Generated with Claude Code

Mir-Zairan · 4 days ago

Still reproducible on 2.1.296 (Linux), and it contradicts the current docs. Related: #81924 (the misleading "not cached" message is what users see here).

Setup: a project's .claude/settings.json:

"extraKnownMarketplaces": { "pitlane": { "source": { "source": "github", "repo": "Mir-Zairan/pitlane", "ref": "v0.8.0" }, "autoUpdate": false } },
"enabledPlugins": { "pitlane@pitlane": true }

The marketplace lists the plugin by relative path ("source": "./"). Fresh CLAUDE_CONFIG_DIR, logged in, folder trusted.

  • Interactive, session 1: the plugin is absent. /plugin → Errors: Plugin "pitlane" not cached at <config>/plugins/marketplaces/pitlane — Run /plugin to refresh the plugin cache. Opening /plugin fills plugins/cache/pitlane, but installed_plugins.json stays {}.
  • Interactive, session 2: still absent, same error. "Enter to resolve" only offers disable for me or uninstall for everyone.
  • claude -p in the same folder (with CLAUDE_CODE_SYNC_PLUGIN_INSTALL=1): emits plugin_install started/completed, writes a project-scope record, loads the plugin, and its hooks run.

An official plugin enabled the same way (frontend-design@claude-plugins-official) shows the same error, so it isn't specific to one marketplace.

The docs (Require plugins per repository) say: "A plugin that the marketplace lists by a relative path loads from the marketplace copy once the repository's extraKnownMarketplaces entries apply." -p does that; interactive sessions don't. Workaround: each developer runs claude plugin install <plugin>@<marketplace> once, or an admin sets the two keys in managed settings.

Showing cached comments. Read the full discussion on GitHub ↗