Plugins: enabled-in-settings but never installed loads nothing silently; marketplace autoUpdate never updates existing installs
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→githubsource), 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)
- In repo A:
/plugin marketplace add <marketplace>then/plugin install some-plugin@<marketplace>(project scope). Commit the resultingenabledPlugins+extraKnownMarketplacessettings. - Clone repo A to a fresh path (or a second machine), or copy the same settings block into repo B.
- Start a session there: the plugin's skills/hooks don't load; nothing reports why.
Steps to reproduce (Bug 2)
- Install a marketplace plugin at version N in a project; set the marketplace
autoUpdate: true. - Publish version N+1 to the marketplace repo; wait for (or trigger) a marketplace refresh — the clone now carries N+1.
- Sessions in the project keep running version N indefinitely;
installed_plugins.jsonkeepsversion: N; no lag signal anywhere.
Showing cached comments. Read the full discussion on GitHub ↗
3 Comments
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": trueon the marketplace entry.Claude Code 2.1.228, Windows (win32 10.0.26100).
Observed
The marketplace registry entry's
lastUpdatedadvances during a session, butinstalled_plugins.jsonrecords never move. Publishing a new plugin version and moving the tracked ref changes nothing for existing installs; only an explicitclaude plugin update <plugin>@<marketplace> --scope project, run once per project directory, moves them.Controls
claude -p; re-measured in a normal interactive session left open 13.8 min (past the documented ≤10 min delay). Same result.lastUpdatedadvanced during it, so the refresh ran and was not throttled — installs still did not move.autoUpdateis honored. It is present as"autoUpdate": trueinknown_marketplaces.json, so the project-level setting does reach the registry.GIT_TERMINAL_PROMPT=0 git ls-remote <repo> <ref>returns without prompting, and a manualgit fetchinside the marketplace clone succeeds and advances the remote-tracking ref.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 whilelastUpdatedwas being stamped. That matches the cache-only path described in #83777.Bug 1 — also reproduced
Independently hit the same silent absence:
enabledPlugins+extraKnownMarketplacescommitted 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 explicitclaude plugin marketplace addwas 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
SessionStarthook 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
SessionStarthook reads the installedplugin.jsonversion, reads the version from the marketplace catalog copy on disk, and when the catalog is ahead it writes astale=<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.Triaged on the current release (v2.1.233, Linux), using a git-source marketplace declared in a project's
.claude/settings.jsonplusenabledPlugins, 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+extraKnownMarketplacescommitted and no prior install record, the marketplace was registered automatically and the plugin's skills loaded and ran — no manual/plugin installneeded. However, the plugin's hooks still don't run in that state (aSessionStarthook 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
autoUpdatenever updates installs): reproduced on v2.1.233. With"autoUpdate": trueon 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). Runningclaude plugin update <plugin>@<marketplace> --scope projectupdated immediately, so only the automatic path is affected. This contradicts the current settings documentation, which saysautoUpdate: truemakes 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
autoUpdatenot updating third-party marketplaces/installs.🤖 Generated with Claude Code
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:The marketplace lists the plugin by relative path (
"source": "./"). FreshCLAUDE_CONFIG_DIR, logged in, folder trusted./plugin→ Errors:Plugin "pitlane" not cached at <config>/plugins/marketplaces/pitlane — Run /plugin to refresh the plugin cache. Opening/pluginfillsplugins/cache/pitlane, butinstalled_plugins.jsonstays{}.claude -pin the same folder (withCLAUDE_CODE_SYNC_PLUGIN_INSTALL=1): emitsplugin_installstarted/completed, writes aproject-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
extraKnownMarketplacesentries apply."-pdoes that; interactive sessions don't. Workaround: each developer runsclaude plugin install <plugin>@<marketplace>once, or an admin sets the two keys in managed settings.