anthropics / anthropics/claude-code
[BUG] A directory marketplace whose recorded installLocation no longer exists never loads again: every launch re-fetches from source but keeps the dead path, and claude plugin marketplace update fails on it
- 主要言語
- Python
- スター
- 145k
- フォーク
- 23.1k
- PR マージ指標
- PR 指標を取得中
説明
### Preflight Checklist
- [x] I have searched existing issues and this hasn't been reported yet
- [x] This is a single bug report
- [x] I am using the latest version of Claude Code
### What's Wrong?
Take a `directory`-source marketplace whose `installLocation` in `known_marketplaces.json` points at a path that no longer exists, while its `source.path` still exists. That marketplace's plugins never load again.
Every launch notices the problem: `Cache corrupted or missing for marketplace mkt-a, re-fetching from source: ENOENT …`. It bumps the entry's `lastUpdated` and **leaves `installLocation` pointing at the dead path**, so the next launch misses in exactly the same way. The file gets rewritten on every launch, just never the field that is wrong.
The CLI doesn't fix it either:
- `claude plugin marketplace update mkt-a` fails on the dead path (`Failed to refresh marketplace 'mkt-a': ENOENT … '/nonexistent/mkt-a'`) instead of resolving from `source`.
- `claude plugin list` shows `✘ failed to load — Marketplace mkt-a failed to load: cache-miss`, and nothing tells the user which command recovers it.
- The one recovery that works, `claude plugin marketplace remove` then `add`, does write a correct `installLocation`. But `remove` also deletes the marketplace's plugins from `enabledPlugins`. After "recovering", the plugin still doesn't load, because it is now disabled.
For a directory source, Claude Code itself records `installLocation` equal to `source.path` when it adds the marketplace. So once the two diverge (for example, the file was carried across machines or config dirs; cf. #36575, #82272), the entry stays stuck, even though the correct value is sitting in the same entry.
### What Should Happen?
1. When a `directory` source is re-fetched because the recorded `installLocation` is missing, write the resolved `source.path` back as `installLocation`.
2. `claude plugin marketplace update` should resolve a directory marketplace from `source`, not from the recorded location.
3. `remove` + `add` should not be the only recovery, given that `remove` drops the marketplace's plugins from `enabledPlugins` (#82272 notes the same side effect).
### Error Messages/Logs
```shell
# every launch (debug log)
[DEBUG] Registered 1 hooks from 1 plugins
[WARN] Cache corrupted or missing for marketplace mkt-a, re-fetching from source: ENOENT: no such file or directory, open '/nonexistent/mkt-a'
$ claude plugin marketplace update mkt-a
Updating marketplace: mkt-a...✘ Failed to update marketplace(s): Failed to refresh marketplace 'mkt-a': ENOENT: no such file or directory, open '/nonexistent/mkt-a'
```
### Steps to Reproduce
Runs against an isolated config dir. No login is needed, because plugin loading runs before the auth check (each `-p` launch exits with `Not logged in`, which does not affect the result).
```bash
# Isolated config dir; no login needed (plugin loading runs before the auth check).
R=$(mktemp -d); mkdir -p "$R/cfg/plugins" "$R/home"
for m in a b; do
d="$R/mkt-$m"; p="$d/plugins/plugin-$m"
mkdir -p "$d/.claude-plugin" "$p/.claude-plugin" "$p/hooks"
echo "{\"name\":\"mkt-$m\",\"owner\":{\"name\":\"demo\"},\"plugins\":[{\"name\":\"plugin-$m\",\"source\":\"./plugins/plugin-$m\"}]}" > "$d/.claude-plugin/marketplace.json"
echo "{\"name\":\"plugin-$m\",\"version\":\"0.0.1\"}" > "$p/.claude-plugin/plugin.json"
echo '{"hooks":{"SessionStart":[{"hooks":[{"type":"command","command":"true"}]}]}}' > "$p/hooks/hooks.json"
done
cat > "$R/cfg/settings.json" < "$KM" </dev/null 2>&1
echo "-- launch $i"; grep -hE 'Registered|Cache corrupted' "$R/launch$i.log" | cut -c26-
grep -o '"installLocation": *"/nonexistent[^"]*"\|"lastUpdated": *"[^"]*"' "$KM" | head -1
done
cc plugin marketplace update mkt-a
cc plugin list
echo "== the CLI recovery: remove + add"
cc plugin marketplace remove mkt-a
grep -A3 enabledPlugins "$R/cfg/settings.json"
cc plugin marketplace add "$R/mkt-a"
cc -p noop --debug-file "$R/launch3.log" >/dev/null 2>&1
grep -h 'Registered' "$R/launch3.log" | cut -c26-
cc plugin list
```
Observed on 2.1.272 (verbatim; ANSI colour codes stripped):
```
== mkt-a's directory source exists, but its recorded installLocation does not
-- launch 1
[DEBUG] Registered 1 hooks from 1 plugins
[WARN] Cache corrupted or missing for marketplace mkt-a, re-fetching from source: ENOENT: no such file or directory, open '/nonexistent/mkt-a'
"installLocation": "/nonexistent/mkt-a"
-- launch 2
[DEBUG] Registered 1 hooks from 1 plugins
[WARN] Cache corrupted or missing for marketplace mkt-a, re-fetching from source: ENOENT: no such file or directory, open '/nonexistent/mkt-a'
"installLocation": "/nonexistent/mkt-a"
Updating marketplace: mkt-a...✘ Failed to update marketplace(s): Failed to refresh marketplace 'mkt-a': ENOENT: no such file or directory, open '/nonexistent/mkt-a'
Installed plugins:
❯ plugin-a@mkt-a
Version: unknown
Scope: user
Status: ✘ failed to load
Error: Marketplace mkt-a failed to load: cache-miss
❯ plugin-b@mkt-b
Version: 0.0.1
Scope: user
Status: ✔ enabled
== the CLI recovery: remove + add
✔ Successfully removed marketplace: mkt-a
"enabledPlugins": {
"plugin-b@mkt-b": true
},
"extraKnownMarketplaces": {
Adding marketplace…✔ Successfully added marketplace: mkt-a (declared in user settings)
[DEBUG] Registered 1 hooks from 1 plugins
Installed plugins:
❯ plugin-b@mkt-b
Version: 0.0.1
Scope: user
Status: ✔ enabled
```
Both launches load only `plugin-b`, and `installLocation` stays dead even though the entry is rewritten each time (its `lastUpdated` changes). After `remove` + `add`, `plugin-a@mkt-a` is gone from `enabledPlugins` and from `claude plugin list`, so the next launch still loads one plugin.
### Claude Model
Not applicable. The failure happens before any model call.
### Is this a regression?
I don't know. I haven't tested older versions.
### Last Working Version
_No response_
### Claude Code Version
2.1.272 (Claude Code)
### Platform
Other. Reproduces without authentication.
### Operating System
Ubuntu/Debian Linux (WSL2)
### Terminal/Shell
bash, non-interactive (`claude -p` and `claude plugin …` subcommands)
### Additional Information
Related, but not the same bug:
- #82272: `installLocation` is validated by string prefix, so a symlinked config dir is flagged as corrupted. Here the path really does not exist, on a single config dir.
- #36575 (closed): non-portable `installLocation` formats.
- #94451: the sibling registry bug, where an invalid `known_marketplaces.json` is never repaired.
コントリビューションガイド
このリポジトリのコントリビューションガイドは索引されていません
評価
この issue はまだ評価されていません。