Settings-driven plugin auto-install (enabledPlugins) writes installed_plugins.json without populating plugins/cache/

Open 💬 0 comments Opened Jul 11, 2026 by matteobortolazzo

Title

Settings-driven plugin auto-install (enabledPlugins) writes installed_plugins.json without populating plugins/cache/

Environment

  • Claude Code version: 2.1.207
  • OS: Linux (reproduced in Ubuntu-based Docker containers; not yet tested on macOS/Windows)
  • Auth: reproduces even fully headless with no login (claude -p "hi")

Summary

When a fresh ~/.claude config directory has enabledPlugins + extraKnownMarketplaces set in settings.json, and the marketplace repo has already been cloned into ~/.claude/plugins/marketplaces/<name> (e.g. by a prior claude plugin marketplace add), starting a session causes Claude Code to write ~/.claude/plugins/installed_plugins.json with entries that look fully installed — correct installPath, correct version, installedAt/lastUpdated timestamps — but it never actually populates the installPath directory under plugins/cache/. The plugin's skills, agents, and hooks are therefore unavailable, and any slash command from that plugin fails with Unknown command: /<plugin>:<skill>.

Because Claude Code trusts installed_plugins.json as the source of truth for "is this installed," nothing ever retries or repairs this — the broken state is permanent until the metadata is manually deleted or the plugin is reinstalled via the CLI.

Steps to reproduce

  1. Start from a config directory where ~/.claude/plugins/marketplaces/<marketplace-name>/ already exists (a prior claude plugin marketplace add <repo> succeeded), but ~/.claude/plugins/cache/ is empty or missing the target plugin.
  2. Set in settings.json:

``json
{
"extraKnownMarketplaces": {
"<marketplace-name>": { "source": { "source": "github", "repo": "<owner>/<repo>" } }
},
"enabledPlugins": { "<plugin>@<marketplace-name>": true }
}
``

  1. Run claude -p "hi" (no interactive session, no login required) against this config directory.
  2. Inspect ~/.claude/plugins/installed_plugins.json.

Expected

Either:

  • plugins/cache/<marketplace-name>/<plugin>/<version>/ is populated with the plugin's files before (or as part of) writing the metadata entry, or
  • the metadata entry is only written after the cache directory is confirmed present.

Actual

installed_plugins.json gets a fully-formed entry (installPath, version, installedAt, lastUpdated, gitCommitSha) pointing at a plugins/cache/... path that does not exist on disk. No error is surfaced. The plugin's skills are unreachable — commands like /<plugin>:<skill> fail with Unknown command, silently and permanently (nothing re-checks or repairs the cache on subsequent runs).

Reproduced deterministically 3 times in throwaway Docker containers with the same config directory shape.

Workaround

Explicitly run claude plugin install <plugin>@<marketplace-name> via the CLI — this correctly populates the cache and rewrites the metadata, even when installed_plugins.json already (incorrectly) claims the plugin is installed. claude plugin marketplace add <repo> is idempotent and safe to re-run as well. We now provision plugins this way explicitly at container-boot time instead of relying on enabledPlugins alone.

Possibly related

  • #76535
  • #75392

View original on GitHub ↗