◉CLAUDE LABJP
●2.1.302 — Fork subagents are now on by default in claude -p, the Agent SDK and the desktop app. Turn them off with CLAUDE_CODE_FORK_SUBAGENT=0 (10/16)●2.1.299 — Conversations over 200K tokens left idle about 54 minutes get compacted; MCP allow rules now match only the server they name●SONNET 4.5 — Retires on the Claude API on 11/30, 43 days left. Migrate to Sonnet 5.5●WINDOWS — Reports say hooks break silently only on Japanese-locale Windows; check the character encoding first●FAST MODE — Opus 5.5 fast mode is out of research preview and no longer needs the beta header (10/15)●NEW — I Put a Ten-Line Promise File at the To…●2.1.302 — Fork subagents are now on by default in claude -p, the Agent SDK and the desktop app. Turn them off with CLAUDE_CODE_FORK_SUBAGENT=0 (10/16)●2.1.299 — Conversations over 200K tokens left idle about 54 minutes get compacted; MCP allow rules now match only the server they name●SONNET 4.5 — Retires on the Claude API on 11/30, 43 days left. Migrate to Sonnet 5.5●WINDOWS — Reports say hooks break silently only on Japanese-locale Windows; check the character encoding first●FAST MODE — Opus 5.5 fast mode is out of research preview and no longer needs the beta header (10/15)●NEW — I Put a Ten-Line Promise File at the To…
Articles/Claude Code
⟐ Claude Code/2026-09-04Intermediate

The ~/.claude.json Rollback Is Fixed in v2.1.259. Putting Back What It Erased Is Still Your Job

Concurrent sessions used to silently roll back each other's ~/.claude.json changes. v2.1.259 fixed that, but nothing restores the trust settings and MCP servers you already lost. Here is how I find and rebuild them, with a small key-path diff script.

Claude Code267MCP56ConfigurationConcurrency3Troubleshooting22

I had one session sorting image assets for my wallpaper app while, in the window next to it, I edited copy for a client site. The next morning I came back to that second window and the MCP servers I had connected were gone from the list, and the workspace trust prompt appeared again as if I had never approved it.

My first suspicion was my own carelessness. I went back through every file I remembered touching the night before, and every line I had written was still there. It took three repeats before I noticed the pattern: what disappeared was always the change made by whichever session had edited settings first.

It was not my habits. Concurrent sessions were silently reverting each other's changes to ~/.claude.json, and it was fixed in Claude Code v2.1.259, released on September 2, 2026.

The short version of what to do now

Three steps, in this order.

  1. Check your version and upgrade if you are below v2.1.259.
  2. Find what is missing and put it back by hand. Nothing does this for you.
  3. Take one snapshot of the config file afterward, so the next disappearance is obvious.
claude --version
# Keep a copy of the current state before you upgrade
cp ~/.claude.json ~/.claude.json.$(date +%Y%m%d-%H%M%S).bak

What the release fixed is future damage. Settings that were already dropped do not come back on their own, so upgrading and calling it done leaves you with quiet holes you will trip over later.

Every symptom looks like something you did wrong

That is what made this expensive for me. I spent three days auditing my own procedure.

What you seeWhat you blame firstWhat points at a rollback
The workspace trust prompt returnsYou reopened the projectYou approved that exact path yesterday
MCP servers vanish from the listA typo in your config fileThe config file itself is untouched
Per-project state resetsA behavior change in the updateThe other open session is unaffected

Before I accepted that explanation I tried the cheap alternatives first, because they are cheap. I re-validated the JSON, which parsed fine. I checked file permissions and ownership, which were unchanged. I diffed my project-level configuration against git, which showed nothing. Each of those took a few minutes and each came back clean, and that accumulating cleanliness was itself the signal: when every input is intact and the output still changes, something outside your inputs is doing the writing.

The last row is what settled it for me. Of the two sessions I had open, exactly one reverted and the other was fine. A malformed config would hurt both equally. One-of-two is much easier to explain as something writing over the file from outside.

Why the later writer wins

~/.claude.json is read when a session starts and written back as a whole when something changes. That read-modify-write shape drops any edit another process made in between, without complaining. Databases have called this a lost update for decades.

The uncomfortable part is that the file it leaves behind is not broken. It is valid JSON, it parses, and nothing raises a warning. The contents are simply forty minutes old.

A corrupted config announces itself at startup; a well-formed but stale config stays quiet until the moment you need the setting.

Misspelled keys have the same shape of invisibility from a completely different cause, which I wrote about in A One-Letter Typo in settings.json Is Ignored Without a Single Warning. Different mechanism, same problem: the file is well-formed, so nothing stops you.

A small script for finding what went missing

The hard part of restoring is remembering what was there. I stopped relying on memory and started recording key paths so I could diff them.

#!/usr/bin/env python3
"""Record the key paths in ~/.claude.json and show what changed since last run."""
import json
import datetime
import pathlib
 
SRC = pathlib.Path.home() / ".claude.json"
SNAP_DIR = pathlib.Path.home() / ".claude-config-snapshots"
MAX_DEPTH = 3
 
 
def key_paths(node, prefix="", depth=0):
    # Values may hold history and credentials, so collect paths only
    paths = set()
    if isinstance(node, dict) and depth <= MAX_DEPTH:
        for key, value in node.items():
            path = f"{prefix}/{key}"
            paths.add(path)
            paths |= key_paths(value, path, depth + 1)
    return paths
 
 
def main():
    if not SRC.exists():
        print(f"Not found: {SRC}")
        return
    SNAP_DIR.mkdir(exist_ok=True)
 
    with SRC.open(encoding="utf-8") as handle:
        current = key_paths(json.load(handle))
 
    snapshots = sorted(SNAP_DIR.glob("*.txt"))
    if snapshots:
        previous = set(snapshots[-1].read_text(encoding="utf-8").splitlines())
        lost = sorted(previous - current)
        gained = sorted(current - previous)
        for path in lost:
            print(f"GONE:  {path}")
        for path in gained:
            print(f"NEW:   {path}")
        if not lost and not gained:
            print(f"No changes ({len(current)} paths)")
    else:
        print(f"First run, nothing to compare ({len(current)} paths recorded)")
 
    stamp = datetime.datetime.now().strftime("%Y%m%d-%H%M%S")
    target = SNAP_DIR / f"{stamp}.txt"
    target.write_text("\n".join(sorted(current)), encoding="utf-8")
    print(f"Recorded: {target}")
 
 
if __name__ == "__main__":
    main()

Two choices in there are worth explaining.

The first is that it reads key paths and never values. This file can hold conversation history and credential material, and I did not want any of that copied outside the file just so I could compare two states. Paths alone still tell you which project lost which setting.

The second is that it does not check for any named field. My first attempt looked directly for the field that records whether a directory has been trusted. But this file is not a published contract, and its shape moves between releases. A check written against a specific name reads a renamed field as "key absent, nothing wrong" and passes in silence — which is exactly the failure mode I was trying to detect. A key-set diff surfaces a rename too, as a matched pair of GONE and NEW lines.

From there I walk the output and rebuild each missing setting. For MCP servers I compare against claude mcp list and re-add what is absent. For trust, opening a session in that directory once will ask again.

One ordering detail saved me time on the second pass. Restore trust for a directory before you re-add MCP servers scoped to it, not after. Servers attached to a directory you have not yet approved will not come up, and you end up debugging a server definition that was never the problem. Approve first, then add, then confirm with claude mcp list in that same directory rather than from your home folder — scope is easy to lose track of when you are rebuilding several projects in one sitting.

It is also worth writing down what you restored, in whatever place you already keep notes. Half of my second morning went to re-deriving a server configuration I had reconstructed correctly the day before and then lost again to the same rollback, because I upgraded only after the second loss.

The boundary I kept after upgrading

I still do these three things, because what got fixed is Claude Code's side of the file — not my own backup and inventory scripts that also touch it.

  • Changes to settings (adding an MCP server, approving trust, installing a plugin) happen in one session only, with the others closed.
  • After any such change I run the script above and leave one snapshot behind. It takes a few seconds.
  • Unattended scheduled jobs may read the config file but never write it. If a job needs to write, I move that job back to an interactive run.

The third rule is the one I expected to drop and did not. Read-only unattended jobs are genuinely useful — they can tell you which servers are configured, or that a directory lost its approval overnight — and keeping them read-only costs nothing until the day a job wants to fix what it found. That is the moment to move it back to an interactive run rather than granting it a write it will perform while three other sessions are open.

One mouth writes the config; any number of mouths may read it. On days when I work in parallel, that is the one line I hold to.

For handing a long job to a second session and waiting on it, I wrote down the arrangement I use in Handing a long job to another session — and the completion marker for when the notification never arrives. And for the same "second process barges in" problem approached from the lock-file side, there is When a lock left in a shared folder shuts out the second unattended run.

Your next step

Run claude --version. If you are below v2.1.259, upgrade, and then run the script once so you have a first snapshot on disk. The next time something disappears, you will spend a minute deciding whether it disappeared instead of an afternoon.

Having burned three mornings on this myself, I hope it shortens someone else's. Thank you for reading this far.

Share

Thank You for Reading

Claude Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • ✦Copy-paste ready implementation code
  • ✦New advanced guides published daily
  • ✦$5/mo or $10 for lifetime access
View Membership →

If you found this article helpful, a small tip ($1.50) would mean a lot to us. Your support helps keep this site ad-free and covers server and hosting costs.

Related Articles

⟐ Claude Code2026-10-19
If You Wrote MCP Allow Rules as Just `mcp__servername` — Three Lines to Recheck After v2.1.299
Claude Code v2.1.299 changed MCP allow rules to match only the server they name. Here is how to audit older entries in settings.json, with a short Python script that does it.
⟐ Claude Code2026-10-18
I Added the allow Rule and the Prompt Still Appears — Reading Six Layers from /permissions
When an allow rule is in your settings and the same approval prompt keeps coming back, something else decided first. Starting from what /permissions shows, here are six layers you can verify against the official permissions docs: ask and deny precedence, where settings.local.json is read, Cowork's shell tool, and more.
⟐ Claude Code2026-10-13
The Same Approval Prompt for 'cd, git add, git commit' Every Time — Following It to a Design Choice, Not a Missing Setting
With a compound command like cd /path && git add && git commit, Claude Code keeps asking for approval even with allow rules in place. Reading the official permissions page next to a GitHub report shows that cd combined with git prompts by design. Here is how to tell, in what order to check, and four ways to cut the prompts.