anthropics / anthropics/claude-code

[BUG] known_marketplaces.json is never repaired once invalid: one entry missing lastUpdated (or a parse error) disables plugins from every marketplace, and the reconciler, marketplace add and marketplace remove all fail re-reading it

未关闭
#94,451 0 条评论 0 个 reaction 已指派 0 人 在 GitHub 查看
area:plugins bug has repro platform:linux platform:wsl
主要语言
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?

When `known_marketplaces.json` fails to load, Claude Code has everything it needs to rebuild it: every marketplace is still declared in `extraKnownMarketplaces`. It even tries. The reconciler logs `failed to load known_marketplaces.json, treating as empty` and reinstalls each declared marketplace. **Every one of those installs fails, because the install's write re-reads and re-validates the same invalid file and aborts.** The file is never repaired, on that launch or any later one, and no plugin from **any** marketplace loads.

Two triggers, same outcome:

1. **One entry missing `lastUpdated`.** The schema check rejects the whole file (`Marketplace configuration file is corrupted: mkt-a.lastUpdated: Invalid input`), so one bad entry disables the plugins of every *other*, fully valid marketplace. The log shows it directly: installing the valid `mkt-b` fails with `mkt-a`'s error.
2. **A JSON parse error** (a truncated file). Same reconciler sequence, same result.

The CLI cannot get out of it either. `claude plugin marketplace remove` and `claude plugin marketplace add` fail with the same error, and `claude plugin list` reports both plugins as `✔ enabled` while every launch loads zero of them. The only recovery is deleting the file by hand. A *missing* file is the one failed state Claude Code does heal: it rebuilds it and the launch after that loads everything.

In my case the entry without `lastUpdated` came from a local repair script, not from Claude Code, which always writes the field. But this file goes invalid through plenty of routes, Claude Code's own included: a UTF-8 BOM from Windows tooling (#84501), a zero-byte file from an interrupted write (#19065), a trailing comma written by `/plugin marketplace add` (#56967). Whatever the route, one bad entry should not cost every plugin, and the repair Claude Code already attempts should be able to land.

### What Should Happen?

1. **Validate per entry.** Skip (or quarantine) an entry that fails the schema and keep loading the rest, rather than rejecting the file.
2. **Let the reconciler's rebuild land.** When the file cannot be loaded at all, move it aside (e.g. `known_marketplaces.json.corrupt-`) and rebuild it from `extraKnownMarketplaces`, instead of routing every install through a write path that re-reads the invalid file and aborts. The reconciler has already decided to treat the file as empty; the writer is the only part that disagrees.
3. **Let `claude plugin marketplace remove` work on an invalid file.** It is the natural manual fix, and today it fails with the same error.

A startup warning (#84501's second ask) would help, but on its own it still leaves every plugin disabled until the user finds and deletes an internal file.

### Error Messages/Logs

```shell
# debug log, second launch; mkt-a's entry lacks lastUpdated, mkt-b's is valid, both declared in extraKnownMarketplaces
[DEBUG] Registered 0 hooks from 0 plugins
[ERROR] reconciler: failed to load known_marketplaces.json, treating as empty: Marketplace configuration file is corrupted: mkt-a.lastUpdated: Invalid input
[DEBUG] [reconcile] 2 marketplace(s): mkt-a(install), mkt-b(install)
[ERROR] [reconcile] failed to install marketplace 'mkt-a': Marketplace configuration file is corrupted: mkt-a.lastUpdated: Invalid input
[ERROR] [reconcile] failed to install marketplace 'mkt-b': Marketplace configuration file is corrupted: mkt-a.lastUpdated: Invalid input

# the same launch also logs
[ERROR] Plugin autoupdate: failed: Marketplace configuration file is corrupted: mkt-a.lastUpdated: Invalid input
[ERROR] syncDeclaredAutoUpdateToJson: failed to update known_marketplaces.json: Marketplace configuration file is corrupted: mkt-a.lastUpdated: Invalid input

# truncated file instead: identical sequence with
[ERROR] reconciler: failed to load known_marketplaces.json, treating as empty: Failed to load marketplace configuration: JSON Parse error: Unterminated string
[ERROR] [reconcile] failed to install marketplace 'mkt-b': Failed to load marketplace configuration: JSON Parse error: Unterminated string
```

### Steps to Reproduce

Runs against an isolated config dir. No login is needed, because plugin loading and the reconciler run 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 and the reconciler run 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; done
grep -hE 'Registered|\[reconcile\]|reconciler:' "$R/launch2.log" | cut -c26-
cc plugin marketplace remove mkt-a
cc plugin marketplace add "$R/mkt-a"
cc plugin list

echo "== 2. truncated file"
printf '%s' '{"mkt-a": {"source": {"sou' > "$KM"
for i in 3 4; do cc -p noop --debug-file "$R/launch$i.log" >/dev/null 2>&1; done
grep -hE 'Registered|\[reconcile\]|reconciler:' "$R/launch4.log" | cut -c26-
cc plugin marketplace remove mkt-a

echo "== 3. control: delete the file"
rm "$KM"
for i in 5 6; do cc -p noop --debug-file "$R/launch$i.log" >/dev/null 2>&1; done
grep -h 'Registered' "$R/launch5.log" "$R/launch6.log" | cut -c26-
```

Observed on 2.1.272 (verbatim; ANSI colour codes stripped):

```
== 1. mkt-a's entry has no lastUpdated; mkt-b's entry is valid
[DEBUG] Registered 0 hooks from 0 plugins
[ERROR] reconciler: failed to load known_marketplaces.json, treating as empty: Marketplace configuration file is corrupted: mkt-a.lastUpdated: Invalid input
[DEBUG] [reconcile] 2 marketplace(s): mkt-a(install), mkt-b(install)
[ERROR] [reconcile] failed to install marketplace 'mkt-a': Marketplace configuration file is corrupted: mkt-a.lastUpdated: Invalid input
[ERROR] [reconcile] failed to install marketplace 'mkt-b': Marketplace configuration file is corrupted: mkt-a.lastUpdated: Invalid input
✘ Failed to remove marketplace: Marketplace configuration file is corrupted: mkt-a.lastUpdated: Invalid input
Adding marketplace…✘ Failed to add marketplace: Marketplace configuration file is corrupted: mkt-a.lastUpdated: Invalid input

Installed plugins:

❯ plugin-a@mkt-a
Version: 0.0.1
Scope: user
Status: ✔ enabled

❯ plugin-b@mkt-b
Version: 0.0.1
Scope: user
Status: ✔ enabled

== 2. truncated file
[DEBUG] Registered 0 hooks from 0 plugins
[ERROR] reconciler: failed to load known_marketplaces.json, treating as empty: Failed to load marketplace configuration: JSON Parse error: Unterminated string
[DEBUG] [reconcile] 2 marketplace(s): mkt-a(install), mkt-b(install)
[ERROR] [reconcile] failed to install marketplace 'mkt-a': Failed to load marketplace configuration: JSON Parse error: Unterminated string
[ERROR] [reconcile] failed to install marketplace 'mkt-b': Failed to load marketplace configuration: JSON Parse error: Unterminated string
✘ Failed to remove marketplace: Failed to load marketplace configuration: JSON Parse error: Unterminated string
== 3. control: delete the file
[DEBUG] Registered 0 hooks from 0 plugins
[DEBUG] Registered 2 hooks from 2 plugins
```

Cases 1 and 2 print the same thing on every further launch; the file is never rewritten. Case 3 is the control: with the file gone, the first launch rebuilds it and the second loads both plugins.

### Claude Model

Not applicable. The failure happens before any model call.

### Is this a regression?

I don't know. In earlier runs of mine, a truncated file with a single declared marketplace was also left unrepaired on 2.1.121, 2.1.140 and 2.1.150. I can't tell when `lastUpdated` became required.

### 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: #84501 (BOM trigger; asks to tolerate it on read and warn at startup), #19065 (empty-file hang), #56967 (Claude Code writing a trailing comma). Those are ways into this state. This report is about why Claude Code's own repair can't get back out of it, and why a single invalid entry takes down every marketplace. Two related CHANGELOG entries fixed other things: 2.1.246 (`claude plugin install` now reports an error on a corrupted file instead of exiting silently) and 2.1.232 (a concurrent-write race).

贡献指南

这个仓库没有索引到贡献指南

评估

这个 Issue 还没有评估数据。

把新 issue 发到你的邮箱

精选适合新手参与的 GitHub issue 摘要。