claude-code - 💡(How to fix) Fix [BUG] `Edit` tool's `replace_all` silently skips matches and consumes adjacent newlines, while reporting success [2 comments, 2 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
anthropics/claude-code#51986Fetched 2026-04-23 07:39:35
View on GitHub
Comments
2
Participants
2
Timeline
8
Reactions
0
Author
Timeline (top)
labeled ×5commented ×2subscribed ×1

Error Message

Error Messages/Logs

Fix Action

Fix / Workaround

Suggested fixes / mitigations

Workaround

Code Example

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.)

---

# 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.

  2. 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.
  3. 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,
  )
  4. Observe the tool's response: "All occurrences were successfully replaced."
  5. 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
RAW_BUFFERClick to expand / collapse

Preflight Checklist

  • I have searched existing issues and this hasn't been reported yet
  • This is a single bug report (please file separate reports for different bugs)
  • 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:

  1. 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.
  2. 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

  1. Create a file at /tmp/replace_all_bug_test.md with the following exact content:

    # 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.
  2. 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.

  3. 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, ) 4. Observe the tool's response: "All occurrences were successfully replaced." 5. 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`.

extent analysis

TL;DR

The replace_all mode in the Edit tool should be modified to accurately report the number of replaced occurrences and preserve the original file structure, including newline characters.

Guidance

  • Modify the success message to report the actual number of replaced occurrences, such as "Replaced 2 of 5 matches", to surface the silent-skip issue.
  • Audit the replacer's newline handling to prevent adjacent newline characters from being consumed during replacement.
  • Consider implementing a post-replace check to verify if the old string still appears in the file and return a warning if it does.
  • Until a fix is implemented, users can verify the replacement results using grep and wc -l to check for silent-skip and newline-consumption issues.

Example

No code snippet is provided as the issue is related to the behavior of the Edit tool, but the suggested fixes can be implemented in the tool's codebase.

Notes

The issue is specific to the replace_all mode and the adjacency of the replacement pattern to bold markdown syntax. The suggested fixes aim to address the silent-skip and newline-consumption issues, but may require further testing and refinement to ensure correct behavior in all scenarios.

Recommendation

Apply the suggested fixes, particularly reporting the actual occurrence count in the success message and auditing the replacer's newline handling, to address the silent-skip and newline-consumption issues. This will provide a more accurate and reliable replacement experience for users.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING