[BUG] Project-scoped plugin enabled in multiple repos never loads on first session after switching repos (single install record, re-stamped after plugin load)

Open 💬 1 comment Opened Jul 3, 2026 by bennyk8y

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.199)

What's Wrong?

When the same plugin is enabled via checked-in .claude/settings.json (extraKnownMarketplaces + enabledPlugins) in two or more repos, the plugin silently fails to load in the first session after every repo switch — all of its skills, agents, hooks, and MCP servers are absent. If you alternate between the repos (the normal workflow the team-onboarding feature is designed for), the plugin effectively never loads.

Three interacting behaviors cause this:

  1. ~/.claude/plugins/installed_plugins.json keeps a single project-scoped entry per plugin, with one projectPath. Multi-project enablement is not representable.
  2. The plugin loader loads a project-scoped plugin only when record.projectPath === cwd.
  3. The startup sync ("Syncing installed_plugins.json with enabledPlugins from all settings.json files" / "settings.json is source of truth") re-stamps the record's projectPath to the current project — but it runs after plugins have already been loaded.

So a session in repo B finds the record pointing at repo A → skips the plugin → then "repairs" the record to B, which breaks the next session in repo A. Ping-pong.

Steps to Reproduce

  1. Two repos, project-a and project-b, each with an identical checked-in .claude/settings.json:

``json
{
"extraKnownMarketplaces": {
"my-marketplace": { "source": { "source": "github", "repo": "org/claude-plugin" } }
},
"enabledPlugins": { "my-plugin@my-marketplace": true }
}
``

  1. Install/trust the plugin from project-a (registry entry becomes {"scope": "project", "projectPath": ".../project-a"}).
  2. cd project-a && claude --debug-file /tmp/a1.log -p "ok" → log shows Loaded N skills from plugin my-plugin. ✅
  3. cd project-b && claude --debug-file /tmp/b1.log -p "ok"no Loaded N skills from plugin my-plugin line, no plugin skills/agents/hooks in the session. ❌ Registry projectPath is now .../project-b.
  4. cd project-b && claude ... again → loads. ✅ (record now matches)
  5. cd project-a && claude ... → missing again. ❌

Evidence from --debug-file

Ordering inside a single startup — load happens before the sync that would have fixed the record:

[DEBUG] Loaded 32 installed plugins from ~/.claude/plugins/installed_plugins.json
[DEBUG] Attempting to load skills from plugin my-plugin default skillsPath: ...   <-- gated by stale projectPath
[DEBUG] Loaded 28 skills from plugin my-plugin                                    <-- only when projectPath matches cwd
[DEBUG] Syncing installed_plugins.json with enabledPlugins from all settings.json files   <-- runs AFTER load
[DEBUG] Updated my-plugin@my-marketplace scope to project (settings.json is source of truth)

The re-stamp is observable as lastUpdated/projectPath changing in installed_plugins.json at session start, one session too late.

Notes

  • User-level enabledPlugins does not stabilize it: with both user and project settings enabling the plugin, the sync's desired state resolves to project scope with projectPath = cwd, so the record keeps flipping.
  • Manually adding two project entries to the registry array (one per repo) survives sessions in the repo whose entry is first, but a session in the other repo rewrites the first entry to the current project and the dedupe pass collapses the now-identical entries back to one.
  • Related but distinct: #45997 (closed) — loader taking [0] from a multi-entry array; here the sync actively collapses to a single entry so the wrong-version symptom becomes a not-loaded-at-all symptom. #63663 (open, "enabledPlugins not persisting across restarts") looks like the same root cause observed without the multi-repo diagnosis.

Expected Behavior

A plugin enabled by the current project's settings should load regardless of which project a shared single install record was last stamped with. Either keep one install record per (plugin, projectPath), or run the settings→registry sync before plugin loading, or have the loader treat "enabled in current project's settings + present in cache" as loadable.

Environment

  • Claude Code 2.1.199 (native install), macOS 14 (arm64)
  • Plugin: private GitHub marketplace, enabled via checked-in project settings in 3 repos

View original on GitHub ↗

This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗