[BUG] settings.json stale-write clobber silently disables installed plugins (continuation of #27941)

Resolved 💬 6 comments Opened Apr 17, 2026 by 0xdhx Closed May 31, 2026

Preflight

  • [x] Searched existing issues — closest match is #27941 (Config file silently reverts user changes — stale write detection logs telemetry but does not prevent overwrite). That issue was auto-closed by github-actions for inactivity and has now been auto-locked, so I'm filing this as the bot's "open a new issue" path requested.
  • [x] Same root cause (saveConfigWithLock stale-write detection logs telemetry without aborting; in-memory cache fallback). Different downstream symptom worth a fresh report: silent plugin disablement.
  • [x] Latest version of Claude Code (2.1.112).

Summary

The same stale-write mechanism documented in #27941 (and in the deeper duplicates #20637, #24578) clobbers enabledPlugins and extraKnownMarketplaces keys in settings.json, which silently disables previously-working plugins on every subsequent fresh CC session. installed_plugins.json is not load-sufficient — CC requires the boolean enablement flag in settings.json itself, so the moment a stale-snapshot session writes those keys back to null, the plugin is dead until manually re-enabled.

This is qualitatively worse than the MCP env-var case in #27941: at least there, claude mcp commands still saw the server. Here, the plugin's hooks/skills/commands are simply absent from new sessions, with no error message.

Reproduction (observed 2026-04-17 on CC 2.1.112)

  1. Install a local plugin: claude plugin marketplace add /path/to/plugin && claude plugin enable myplugin@my-marketplace. Verify installed_plugins.json and the two settings.json keys are populated.
  2. Have a long-lived CC session running that started before step 1 (so its in-memory snapshot lacks the new keys). Trigger any routine mutation in that session — /effort, /model, permission grant, etc.
  3. The atomic rename overwrites settings.json with the stale snapshot → both keys go null. The plugin's directory and installed_plugins.json entry are untouched.
  4. Start a fresh claude -p "hi" --debug --debug-file=/tmp/debug.log session. Debug log shows:

``
Loaded 20 installed plugins from .../installed_plugins.json
Found 6 plugins (6 enabled, 0 disabled)
Registered 1 hooks from 6 plugins
``
14 of 20 installed plugins are silently disabled — not a single warning surfaces in stdout, stderr, or the regular log. The plugin's SessionStart hooks never fire; its skills/commands are unavailable.

Why this is hard for users to detect

  • No error message, no warning, no log line at WARN/ERROR level.
  • claude plugin list continues to show the plugin (it reads from installed_plugins.json).
  • Plugin functionality just stops appearing in new sessions. Users only notice when something they expected to work doesn't.
  • Existing sessions keep working because plugin registration is loaded at session init (per documented hot-reload behavior); only fresh sessions are affected, so the symptom is intermittent and tied to "did I open a new terminal."

Proposed fix (cheaper of the #27941 options)

Re-read settings.json from disk before write in BOTH the file-lock path AND the cache fallback path. The original report's three-way merge is the correct fix but invasive — even a "re-read on stale detection, retry mutation against fresh content" loop would prevent the clobber here. Currently the stale detection just emits tengu_config_stale_write telemetry and proceeds with the stale write.

Workaround (in production today)

Shell wrapper around claude that runs the heal commands when keys are missing:

claude() {
  local canonical="$HOME/.ccs/shared/settings.json"
  if ! jq -e '.enabledPlugins["myplugin@my-marketplace"] == true and (.extraKnownMarketplaces["my-marketplace"].source.path // empty) != ""' "$canonical" >/dev/null 2>&1; then
    command claude plugin marketplace add /path/to/plugin >/dev/null 2>&1
    command claude plugin enable myplugin@my-marketplace >/dev/null 2>&1
  fi
  command claude "$@"
}

Effective but clearly not where this logic should live.

Environment

  • CC 2.1.112
  • Linux (WSL2 Ubuntu)
  • Multi-session usage (CCS — Claude Code Switch — running ~20 concurrent CC sessions; the long-lived stale-snapshot pool is what makes the clobber happen so often)

References

  • #27941 — original report, root cause analysis with code trace, auto-closed and auto-locked
  • #20637 — multi-window race condition variant
  • #24578 — Edit-tool wipe variant
  • #27247 — confirms enabledPlugins is load-gating (not cosmetic) — settings.local.json entry alone doesn't enable
  • #9537 — plugin marketplace removal doesn't clean settings.json (related plugin-keys lifecycle bug)

View original on GitHub ↗

This issue has 6 comments on GitHub. Read the full discussion on GitHub ↗