[BUG] Plugin update leaves a truncated version dir in the plugin cache (81 of 108 skills missing) when the same plugin@version is installed at user scope and project scope
Summary
After a plugin auto-update, the versioned cache directory under ~/.claude/plugins/cache/<marketplace>/<plugin>/<version>/ contained only a subset of the plugin's files. 27 of 108 skills/* directories and the whole of one other top-level directory were missing, while agents/, hooks/, scripts/, .mcp.json, plugin.json and README.md were copied completely. The marketplace clone under ~/.claude/plugins/marketplaces/<marketplace>/ was complete and at the expected commit, so the loss happened during the marketplace → cache copy, not during the git fetch.
Sessions then loaded the plugin without error and simply exposed 27 skills instead of 108. There was no diagnostic anywhere: no warning at startup, /reload-plugins reported the reduced count as if it were correct, and slash-command autocomplete just lacked the missing skills.
The same plugin's cache dirs for the four previous versions are all complete. One much older version dir on the same machine is also truncated (60 skills), so this has happened at least twice.
Environment
- Claude Code 2.1.272, macOS (Darwin 25.6.0), Claude Desktop "Code" tab and CLI sessions on the same machine
- Plugin from a GitHub-sourced marketplace (
source: github), plugin path./plugins/<name>inside the marketplace repo - Plugin size: 108 skills, 45 agents, ~900 files
- The plugin is installed both at user scope and at project scope (several projects, including git worktrees), all entries pointing at the same
installPath
What installed_plugins.json shows
Two entries for the same plugin@marketplace, same version, same installPath, lastUpdated 2 seconds apart:
{
"scope": "project",
"projectPath": "/path/to/project-a",
"installPath": "~/.claude/plugins/cache/<mp>/<plugin>/0.27.6",
"version": "0.27.6",
"lastUpdated": "2026-09-15T09:42:22.182Z"
},
{
"scope": "user",
"installPath": "~/.claude/plugins/cache/<mp>/<plugin>/0.27.6",
"version": "0.27.6",
"lastUpdated": "2026-09-15T09:42:24.435Z"
}
The cache dir's mtime matches these timestamps. Both updates appear to have (re)written the same directory within the same two-second window.
Evidence
$ ls ~/.claude/plugins/marketplaces/<mp>/plugins/<plugin>/skills | wc -l
108
$ ls ~/.claude/plugins/cache/<mp>/<plugin>/0.27.6/skills | wc -l
27
$ ls ~/.claude/plugins/cache/<mp>/<plugin>/0.27.5/skills | wc -l
108
$ diff <(cd marketplaces/.../<plugin> && find . -type f | sort) \
<(cd cache/.../0.27.6 && find . -type f -not -path './.in_use/*' | sort) | grep -c '^<'
340
Missing files broke down as 82 under skills/ and 12 under one other directory; no files were missing from agents/, hooks/, scripts/, or the plugin root. The surviving 27 skill dirs are not alphabetically contiguous, so this is not a simple "copy stopped at entry N".
The marketplace clone was at the commit recorded in gitCommitSha, with a clean working tree.
Hypothesis
Two install/update code paths (user scope and project scope, or two sessions triggering auto-update) ran concurrently against the same <version> directory. One removed and recreated the directory while the other was mid-copy, so the final tree is whichever subset of the second copy landed after the first one's delete. The per-directory pattern (some top-level dirs complete, skills/ two-thirds empty) is consistent with an rm -rf racing an ongoing recursive copy.
I cannot confirm this from source; it is the only explanation I found that fits the timestamps and the file pattern.
Expected behaviour
- The versioned cache dir should either be complete or not exist. Copy into a temp dir and atomically rename into place, and/or hold a lock per
<plugin>/<version>during install/update. - If multiple scopes resolve to the same
installPath, updating one should not rewrite the dir while another session may be copying into it. - After copying, verify the result against the source (file count or manifest) and fail loudly rather than registering a partial install.
- At session start, if the resolved install dir is missing components the manifest or source declares, emit a warning instead of silently loading fewer skills.
Workaround
Refill the cache dir from the marketplace clone without touching existing files, then /reload-plugins:
rsync -a --ignore-existing \
~/.claude/plugins/marketplaces/<mp>/plugins/<plugin>/ \
~/.claude/plugins/cache/<mp>/<plugin>/<version>/
Deleting the version dir and reinstalling also works once no session holds an .in_use lease. To avoid recurrence I am consolidating to a single (user-scope) install so no two scopes share one installPath.
Related
- #85278 — duplicate
installed_plugins.jsonentries from multiple worktrees (registry side of the same multi-scope fragility) - #83168 — plugin agents intermittently not registered while skills load (different symptom, similarly silent)
- #82145 —
.in_useleases never reaped (blocks the "delete and reinstall" workaround)
This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗