CLAUDE LABJP
FORK — Claude Code 2.1.212 changes what /fork does: it copies your conversation into a new background session with its own row in claude agents, so you can keep working. The old in-session subagent is now /subtaskLIMITS — WebSearch calls are now capped at 200 per session by default, and subagent spawns get the same 200 ceiling, so a runaway search or delegation loop stops on its ownMCPBG — MCP tool calls running past two minutes now move to the background automatically, keeping the session usable. Tune the threshold with CLAUDE_CODE_MCP_AUTO_BACKGROUND_MSPLANFIX — Fixed plan mode auto-running file-modifying Bash commands such as touch and rm without a permission prompt or an SDK canUseTool callbackSONNET5 — Claude Sonnet 5 is running on introductory pricing of $2 per million input tokens and $10 per million output. After August 31 it moves to $3 and $15IPO — Bankers are reportedly lining up investor meetings for Anthropic ahead of a possible public listing as soon as OctoberFORK — Claude Code 2.1.212 changes what /fork does: it copies your conversation into a new background session with its own row in claude agents, so you can keep working. The old in-session subagent is now /subtaskLIMITS — WebSearch calls are now capped at 200 per session by default, and subagent spawns get the same 200 ceiling, so a runaway search or delegation loop stops on its ownMCPBG — MCP tool calls running past two minutes now move to the background automatically, keeping the session usable. Tune the threshold with CLAUDE_CODE_MCP_AUTO_BACKGROUND_MSPLANFIX — Fixed plan mode auto-running file-modifying Bash commands such as touch and rm without a permission prompt or an SDK canUseTool callbackSONNET5 — Claude Sonnet 5 is running on introductory pricing of $2 per million input tokens and $10 per million output. After August 31 it moves to $3 and $15IPO — Bankers are reportedly lining up investor meetings for Anthropic ahead of a possible public listing as soon as October
Articles/Claude Code
Claude Code/2026-05-24Intermediate

When Claude Code's Sequential Edits Cascade into Failure — Fixing 'old_string not found' and 'not unique' on the Same File

Run several Edits against the same file in one turn and the second or third one suddenly fails with 'not found' or 'not unique'. The cause is order-dependence inside a single context snapshot, and a Read + scoped patches usually rescue it.

claude-code129edit-tool3troubleshooting87error17diff

A while back I asked Claude Code to "tidy up three spots in this function" and watched the first Edit go through cleanly, only to see the second one fail with String to replace not found in file and the third trip on String to replace ... is not unique. The spots had been there a second ago. This pattern shows up often enough that it deserves its own playbook.

I'm Masaki Hirokawa, an artist and indie developer running wallpaper and meditation apps that have crossed 50 million downloads since I started in 2014. Claude Code handles most of my day-to-day coding now, from SwiftUI refactors to MDX article swaps for my own Lab sites, and this cascading-Edit failure mode has bitten me more times than I'd like to admit. The root cause almost always reduces to one of two patterns, and the rescue path is mechanical once you spot it.

What's actually happening — Edit doesn't re-Read between calls

The thing worth internalising first is that Claude Code's Edit tool does not transparently re-Read the file after the previous Edit applied. When the model emits several Edits in one turn, the picture of the file in its head and the bytes on disk drift further apart with each successful patch.

The sequence looks like this:

  1. Claude reads the file once with Read.
  2. Based on that snapshot, it generates old_string values for Edits 1, 2, and 3 in a single batch.
  3. Edit 1 applies and the targeted text on disk turns into its new_string.
  4. Edit 2's old_string was built from the Read snapshot, which no longer matches the file.
  5. If Edit 1 deleted lines that Edit 2 depended on, the old_string simply isn't there anymore.
  6. If Edit 1's new_string introduced a copy of the pattern Edit 2 targets, the second old_string is no longer unique.

In other words, Edits are applied serially, but their old_string values were all minted against the same stale snapshot. Once you see it that way, the fixes are mostly mechanical.

Case 1: Edit 1 deletes what Edit 2 is looking for

This is the most common shape I run into. Picture a SwiftUI view with three near-identical modifier chains. Claude decides to delete one .padding(.top, 16) and then rewrite another from .padding(.top, 16) to .padding(.top, 24). By the time Edit 2 fires, the literal string Edit 2 was hunting for is already gone.

Error: String to replace not found in file.
String:     .padding(.top, 16)

I trip on this constantly during AdMob-related SwiftUI work, where helper-function call sites cluster together and Claude tries to "delete the upper one and rewrite the lower one" in a single sweep.

Fix: tell Claude to slip a Read in

My habit when this fires is to reply with a one-liner: "Please Read the file again and then run only the remaining Edits." That makes Claude refresh its view of the file and regenerate the old_string values for whatever's left.

If you want to avoid the cascade in the first place, ask differently up front:

  • Risky framing: "Make all three edits in one go."
  • Safe framing: "Apply the edits one at a time, top to bottom. After each Edit, Read the file and verify the result before moving on."

The second framing slows things down, but for big refactors or structural changes it's the saner mode.

Case 2: Edit 1's new_string multiplies the pattern Edit 2 looks for

The other shape that surfaces a lot is Edit 1 inserting text that contains the very pattern Edit 2 wants to match, which inflates the file's match count.

Error: String to replace is not unique in the file.
Multiple matches found for: const handleSubmit = async () =>

For example: "Split the body of const handleSubmit = async () => and have it call an internal helper of the same shape." If Edit 1's new_string includes the same function signature on the helper side, suddenly there are two call sites where there used to be one, and any later Edit aimed at the original signature can't pick a row.

Fix: widen old_string with surrounding context until it's unique

When not unique fires, ask Claude to "regenerate old_string including the 3-5 lines around the target so it disambiguates, then retry." Edit tooling is strict literal matching, so extending the window always resolves the ambiguity.

I keep a rule in my CLAUDE.md that says "always include closing braces when editing the last function in a file, and always include neighbouring import lines when editing imports." That alone cut my not unique rate noticeably.

# Excerpt from CLAUDE.md
 
When using the Edit tool, make sure `old_string` is unique by including
a few surrounding lines. Especially for import statements, return blocks,
and empty braces, uniqueness is the top priority.

Case 3: a formatter ran between Edits and changed the diff

This shows up less often but stings harder. If a PostToolUse hook fires a formatter after Edit 1 — Prettier, SwiftFormat, gofmt, ruff — indentation can normalise the moment the patch lands, and Edit 2's old_string (still indented the old way) no longer matches.

I lost half a day to this once when I had SwiftFormat wired into PostToolUse for every Edit in one of my iOS app projects.

Fix: don't run the formatter mid-cascade

My current setup runs the formatter only after a batch of Edits is fully settled. Practically, I moved the hook off PostToolUse per-Edit and onto Stop or SubagentStop, so the file isn't rewritten under Claude while the cascade is still in flight.

{
  "hooks": {
    "Stop": [
      { "command": "swiftformat ." }
    ]
  }
}

If you really want a per-Edit formatter, disable the PostToolUse hook temporarily via /hooks during the refactor and re-enable it afterwards.

Triage flow — the order I actually run through

When an Edit cascade breaks, my head walks through this list:

  1. Read the error: is it not found or not unique?
  2. If not found, check whether the previous Edit touched the same region.
  3. If not unique, eyeball whether the recent new_string introduced a copy of the pattern.
  4. If neither, run /hooks and see if a PostToolUse hook is rewriting the file behind the scenes.
  5. Once the cause is clear, tell Claude: "Re-Read the file, then apply the remaining Edits."

Steps 2 and 3 catch most cases. After a while you develop a feel for "how many Edits per Read is safe in this file," and you stop generating cascades in the first place.

Prevention — refactor splits that don't cascade

A few habits I lean on to keep Edit chains from collapsing:

Cap Edits per file at three per turn. If a change would need more, ask Claude to handle one file at a time, with a Read after each, before moving to the next file.

Separate structural Edits from body Edits. Changing a function signature and rewriting its body in the same turn invites the cascade. Do the signature first, confirm, then do the body in a second turn.

Push wide replacements down to Bash + sed. Renaming a symbol across ten or more sites is what sed -i 's/old/new/g' is for. I have a CLAUDE.md note that says "four or more identical replacements: use sed," and Claude picks the right tool from there.

Next step

When you hit this in real time, the single most useful thing you can say is "Re-Read the file, then continue." That clears about seven out of ten cases. The remaining three either need wider old_string context or a paused formatter hook.

I still walk into the cascade trap most weeks, but holding the mental model of "all old_strings come from one Read" makes the rescue almost reflexive. If you've been losing flow to the same error, I hope this saves a little time. Thanks for reading.

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-05-02
Stop the 'old_string is not unique' Error in Claude Code's Edit Tool — A Practical Rewrite Strategy
When Claude Code's Edit tool throws 'old_string is not unique in the file', the root cause is usually that the prompt didn't guarantee a uniquely identifiable target. Here's how to design rewrites that actually stick.
Claude Code2026-06-10
When git add -A Sweeps Up .bak Backups — The Quiet Trap of In-Place Fix Scripts
How .bak backups left by sed -i and homegrown --fix scripts get silently swept into production by git add -A, why it is so easy to miss, and how scoped adds and .gitignore close the gap for good.
Claude Code2026-06-09
When Yesterday's Article Bleeds Into Today's — The Stale Fixed-Name Temp File Trap
In a Claude Code automation pipeline, a fixed-name temp file kept stale content from the previous run and leaked old body text into a completely different article. Here is why the write fails silently, and how unique names plus a post-write grep check prevent it.
📚RECOMMENDED BOOKS
Build a Large Language Model (From Scratch)
Sebastian Raschka
LLM Dev
Prompt Engineering for LLMs
Berryman & Ziegler
Prompting
AI Engineering
Chip Huyen
AI Eng
* Contains affiliate links
See all →