plugin marketplace update fetches new version but doesn't update installed_plugins.json
Status Fixed / completed
Maintainer reply None cached
Workaround ✓ Mentioned in thread ↓
Activity 4 comments · opened Jul 12, 2026 · closed Aug 17, 2026
Bug
claude plugin marketplace update <plugin> downloads the new plugin version into the cache directory but does not update the installPath and version fields in ~/.claude/plugins/installed_plugins.json. New sessions continue using the old version.
Reproduction
- Publish a new version of a plugin (bump version in
plugin.json, push to git) - Run
claude plugin marketplace update <plugin-name> - Start a new Claude session
Expected: New skills/features from the updated plugin are available.
Actual: Session loads the old version. New skills missing.
Root cause
After update, the cache has both versions:
~/.claude/plugins/cache/bat-skills/bat-skills/1.20.0/skills/ # old — 7 skills
~/.claude/plugins/cache/bat-skills/bat-skills/1.21.0/skills/ # new — 10 skills (3 added)
But installed_plugins.json still points to the old version:
"bat-skills@bat-skills": [
{
"installPath": "…/cache/bat-skills/bat-skills/1.20.0",
"version": "1.20.0",
…
}
]
Workaround
Manually edit ~/.claude/plugins/installed_plugins.json — update installPath and version to the new version directory.
Environment
- Claude Code CLI (latest as of 2026-07-12)
- Linux (Fedora 44)
- Plugin: custom marketplace plugin with
plugin.jsonversioning
Showing cached comments. Read the full discussion on GitHub ↗
3 Comments
Additional finding: cache directory not created for latest version
After running
claude plugin marketplace update, the cache directory for the new version was never created either.State after update:
Meanwhile
installed_plugins.jsonwas updated to point to1.22.0(via manual fix from original bug), but the directory doesn't exist.So two bugs in the update flow:
installed_plugins.jsonversion pointer not updated after fetchWorkaround: Manually copy the plugin repo into the cache:
Workaround script
Wrote a script that detects and fixes both issues described above by cross-checking each installed plugin against its marketplace repo clone (
~/.claude/plugins/marketplaces/<marketplace>/):cache/, copies it there (fixes the "never fetched" issue)installed_plugins.jsonpoints at a stale version/path, repoints it at the correct cache dir (fixes the original report)Dry-run by default, backs up
installed_plugins.jsonbefore writing with--apply. Skips externally-sourced plugins (git-subdir/url sources, fetched via a different mechanism) and unversioned plugins (can't reliably reconstruct their cache dir name).Confirmed it catches this live — running
claude plugin marketplace updateon my machine silently leftunderstand-anythingon2.6.3while the marketplace had moved to2.9.4, with no2.9.4directory ever created undercache/. Script fetched it and repointed the entry correctly.Some measured evidence on this from a repo that distributes a plugin through a marketplace entry. CLI
2.1.228, Linux. Everything below was taken in a scratchHOMEwith all Claude credential variables unset.Identity lands in two independent fields, with undocumented and non-exclusive population rules, and only one of them is read by the update comparator.
Two installs of the same plugin, on the same day, at different delivered commits:
The
versionstring is a compound, and only its leading half varies with content. The 12-character half equals the delivered commit both times (confirmed againstgit ls-remote … HEAD). The 8-character half is byte-identical across two different delivered commits, so it does not identify the content it appears to identify. What it encodes is not established here and I am not guessing.Separately, a pre-existing install on the same machine carried
version: 0.0.0-devand a valid 40-charactergitCommitShaat the same time. So the two fields are not alternatives: a consumer cannot infer from the presence of one that the other is absent or stale.Why that bears on this issue.
claude plugin updatecompares version strings. An entry whose recordedversionis constant compares equal on every run, so the update short-circuits and reports success having delivered nothing — with no error and no visible symptom — while a correctgitCommitShasits beside it in the same record showing the install is stale. That is the shape this issue describes, and the metadata is what makes it invisible.One more property, in case it saves someone a debugging cycle:
claude plugin list --jsonis a projection ofinstalled_plugins.json, not an independent authority. Mutating the file to a sentinelversionandinstallPathchanged the CLI's output verbatim, and restoring the file restored the output. So reading the CLI does not let a check escape the metadata — worth knowing if you are tempted to verify an install by asking the CLI rather than by comparing delivered bytes.Happy to provide the raw readings or the reproduction steps if useful.