[BUG] `Edit` tool's `replace_all` silently skips matches and consumes adjacent newlines, while reporting success
Preflight Checklist
- [x] I have searched existing issues and this hasn't been reported yet
- [x] This is a single bug report (please file separate reports for different bugs)
- [x] I am using the latest version of Claude Code
What's Wrong?
The Edit tool's replace_all=true mode silently skips some matches and
damages surrounding structure on others, while reporting "All occurrences
were successfully replaced."
Two distinct failure modes co-occur on a minimal markdown test case:
- Silent skip. Matches whose left-context is a closing
**followed
by a space (i.e. the pattern appears in a **phrase** **marker**
markdown layout) are not replaced. The success message makes no mention
of this.
- Newline consumption. On the matches that do replace, adjacent
newlines are also consumed, merging lines that should have remained
distinct.
Combined, a standard "replace, trust success, move on" workflow produces
(a) unreplaced content the user believes was replaced, and (b) merged
lines the user never intended.
What Should Happen?
All occurrences of the pattern should be replaced. The file's line
structure should be unchanged aside from the removed pattern characters
(same line count, same blank-line separators between sections).
If the tool cannot replace all matches for any reason, the success
message should reflect that -- e.g. "Replaced 2 of 5 occurrences"
rather than an unconditional "All occurrences were successfully
replaced."
Error Messages/Logs
The file /tmp/replace_all_bug_test.md has been updated. All occurrences were successfully replaced.
(The message claims success despite only 2 of 5 matches being replaced and the replaced matches causing unintended line merges.)
Steps to Reproduce
- Create a file at
/tmp/replace_all_bug_test.mdwith the following exact content:
```markdown
# Test file for replace_all gotcha
## Headings with WEAK suffix
### Heading one [WEAK]
### Heading two [WEAK]
## Inline bullets with bold-close adjacent to WEAK
- bolded phrase [WEAK]: content here.
- another phrase [WEAK]: more content.
- third phrase: no WEAK here (control).
- fourth phrase [WEAK]: last one.
- Verify the pre-edit state. Grep the file for the literal pattern
[WEAK] (leading space + [WEAK]). Expected: 5 occurrences
-- 2 on ### heading lines (lines 5 and 6), 3 on bulleted lines
starting with - ** (lines 10, 11, and 13). The bulleted line with
no WEAK suffix (line 12) is a control.
- Read the file (required before Edit), then fire:
Edit(
file_path="/tmp/replace_all_bug_test.md",
old_string=" [WEAK]",
new_string="",
replace_all=true,
)
- Observe the tool's response: "All occurrences were successfully replaced."
- Run post-edit verification:
- Grep for WEAK in the file.
- Read the full file content.
- Compare line counts (pre-edit: 14; expected post-edit: 14; actual: 11).
Observed post-edit state:
- Grep finds 3 remaining [WEAK] occurrences, all on inline-bullet
lines (those preceded by a bolded phrase pattern, i.e.
bold-close adjacent to the replaced pattern's leading space).
- The two ### Heading lines are merged onto one line:
### Heading one### Heading two.
- The blank line separating the heading section from the ## Inline bullets heading has been consumed.
- File shrank from 14 lines to 11 lines (3 lines lost).
Expected post-edit state:
- 0 remaining [WEAK] occurrences in the file.
- Both ### Heading one and ### Heading two remain on separate lines.
- The blank line between the headings section and the ## Inline bullets
heading is preserved.
- Line count unchanged from pre-edit.
Claude Model
Opus
Is this a regression?
I don't know
Last Working Version
_No response_
Claude Code Version
2.1.117
Platform
Anthropic API
Operating System
macOS
Terminal/Shell
Terminal.app (macOS)
Additional Information
## Environment
- macOS, Darwin 25.3.0
- Claude Code 2.1.117 (latest as of 2026-04-22)
## Cross-session verification
Reproduced 2026-04-22 in two independent sessions on the same machine:
first on 2.1.116 (original observation during a bulk markdown cleanup
operation on a different, larger file), then independently on 2.1.117
using the exact minimal test case above in a fresh session with no
shared conversation state and a new `/tmp/` file path. The cross-session
reproduction rules out session-state, conversation-context carry-over,
or file-state artefacts as causes -- this is a tool-level behaviour.
## Impact
- `replace_all` is a commonly-used bulk operation; users trust the
success message and typically do not re-verify.
- **Silent-skip** produces content inconsistency -- unreplaced patterns
the user believes were replaced, discoverable only by grepping after
the edit.
- **Newline-consumption** produces structural damage that is harder to
spot in diff review: the matches that "succeeded" did replace, just
with unadvertised collateral damage (adjacent line merges). A user
scanning the diff for "did the pattern get removed?" sees yes and
moves on.
- Affects any workflow that bulk-cleans markdown with bold-marker
syntax: release-note tagging, review-state markers, TODO annotations,
inline-marker redaction. If a similar adjacency pattern occurs in
source code (uncommon but possible with certain decorator or type-
annotation syntaxes), the line merge could introduce syntax errors
that escape a casual diff review.
## Suggested fixes / mitigations
1. **Report the actual occurrence count** in the success message
(`"Replaced 2 of 5 matches"`). This alone would surface the silent-
skip half of the bug at user-read time, with no behaviour change
in the underlying replacer.
2. **Audit the replacer's newline handling** on matches that did
succeed. Adjacent `\n` characters should not be consumed.
3. Consider a tool-side post-replace check: if the user's `old_string`
still appears in the file after `replace_all`, return a warning
rather than unconditional success. Cheap backstop.
## Workaround
Until a fix lands: after any `replace_all` call, verify with both:
- `grep` to confirm the pattern is actually gone (catches silent-skip).
- `wc -l` to confirm the line count delta matches what the pattern
alone should remove -- usually zero (catches newline-consumption).
For patterns adjacent to bold markdown, prefer per-instance targeted
Edits rather than `replace_all`.This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗