[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

Status Open
Reported on v2.1.272
Maintainer reply None cached
Activity 0 comments · opened Sep 15, 2026

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-<timestamp>) 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

# 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).

# 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" <<EOF
{"enabledPlugins":{"plugin-a@mkt-a":true,"plugin-b@mkt-b":true},
 "extraKnownMarketplaces":{"mkt-a":{"source":{"source":"directory","path":"$R/mkt-a"}},
                           "mkt-b":{"source":{"source":"directory","path":"$R/mkt-b"}}}}
EOF
KM="$R/cfg/plugins/known_marketplaces.json"
cc() { HOME="$R/home" CLAUDE_CONFIG_DIR="$R/cfg" claude "$@"; }

echo "== 1. mkt-a's entry has no lastUpdated; mkt-b's entry is valid"
cat > "$KM" <<EOF
{"mkt-a":{"source":{"source":"directory","path":"$R/mkt-a"},"installLocation":"$R/mkt-a"},
 "mkt-b":{"source":{"source":"directory","path":"$R/mkt-b"},"installLocation":"$R/mkt-b","lastUpdated":"2026-01-01T00:00:00.000Z"}}
EOF
for i in 1 2; do cc -p noop --debug-file "$R/launch$i.log" >/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).

View original on GitHub ↗