VS Code extension /model toggle intermittently corrupts settings.json (malformed JSON), silently dropping all permissions and hooks
Summary
The in-app model switcher (/model toggle in the VS Code extension) intermittently writes malformed JSON to ~/.claude/settings.json when persisting the model selection. When this happens, Claude Code shows "Settings file failed to parse: … Invalid or malformed JSON. Permission rules and other settings from this file are not in effect." — silently dropping all permission rules and hooks for every subsequent session until the file is repaired by hand.
Environment
- Claude Code 2.1.209, VS Code extension (native), VS Code 1.129.1
- Windows 11 Home 10.0.26200
- Model switching between
claude-fable-5[1m]andclaude-opus-4-8 autoUpdatesChannel: "latest"
Repro
- Have a valid
~/.claude/settings.jsonwith permission rules and hooks (ours is ~220 lines). - Switch models via the in-app
/modeltoggle (Fable ↔ Opus). Repeat across sessions. - Intermittently (three separate corruption events between 2026-07-13 and 2026-07-20), the file is left invalid.
Observed corruption shapes (3 events, 2 distinct shapes)
Shape A (2× on 2026-07-13/14): the "model" key written after the root closing brace, plus a duplicate trailing }. Both times the value was claude-fable-5[1m] (the 1M bracket variant), suggesting the writer may mishandle the newer bracketed model IDs.
Shape B (2026-07-20): a missing comma between two keys inside a pre-existing nested hook object ("type": "command" ↔ "command": … inside a Stop hook entry) — i.e. the rewrite mangled a part of the file unrelated to the model key. The same write also re-added "model" (this time inside the object).
Impact
- All
permissions.allowrules (130+) and all hooks silently stop applying. - The failure mode is dangerous: the user sees only a dismissable banner, while the security/permission posture of every session has changed.
Workaround
Pin the model via the ANTHROPIC_MODEL user env var, delete the model key, and never touch the in-app toggle (a wrapper script does parse -> mutate -> re-serialize instead). This works but loses in-session model switching entirely.
Suggested fix
Persist the model selection with a full JSON parse -> mutate -> serialize (or at least validate the file after writing and roll back on parse failure), rather than whatever partial/append write produces the shapes above.