Skip to content

Regex replacement strategy in edit tool treats vertical whitespace as indentation #18591

@werdnum

Description

@werdnum

What happened?

Gemini CLI frequently incorrectly inserts newlines interspersed between lines of code.

What did you expect to happen?

Gemini does not do this

Client information

Google internal user

Login information

No response

Anything else we need to know?

I added a hook to block it, but it never triggered.

I exported the state, and asked Gemini to analyze it. We found that the tool input is correct so it is NOT a model issue. When I got Gemini to dig into the code, it told me this:

The newline insertion bug is located in the calculateRegexReplacement function within packages/core/src/tools/edit.ts.

The Cause

The bug stems from how the tool identifies and captures indentation when using its "regex" replacement strategy. The code uses the following regular expression to find the target block and its leading indentation:

   // packages/core/src/tools/edit.ts:229
   const finalPattern = ^(\\s*)${pattern};
   const flexibleRegex = new RegExp(finalPattern, 'm');

In JavaScript's regex engine (which the CLI uses), \s matches all whitespace, including newlines (\n).

When the regex strategy is triggered (usually because the exact match failed due to minor whitespace differences):

  1. The regex captures everything from the start of a line matching the pattern.
  2. If there are blank lines immediately preceding the code you are replacing, the ^(\s*) capture group gobbles up those newlines and treats them as part of the "indentation."
  3. The tool then iterates through every line of your new_string and prepends this "indentation" (which now includes \n characters) to each one.

The Impact

If the captured indentation contains, for example, two newlines (\n\n), and your new_string is 10 lines long, the tool will effectively insert 20 extra blank lines into the file. This explains why the "silly edit blocker" hook didn't catch it—the new_string passed into the tool was perfectly normal, but the tool
corrupted the content while applying the replacement.

Why it triggered here

In the edit you mentioned, the agent was replacing several Python test methods. There were likely blank lines between the methods in the source file. The calculateRegexReplacement strategy likely captured the blank line preceding the method and incorrectly distributed it throughout the entire replacement block.

                                                                                                                                                                                                                                                                                                                             The Fix                                                                                                                                                                                                                                                                                                                        The capture group should be restricted to horizontal whitespace (spaces and tabs) only:
   // Replace this:
   const finalPattern = ^(\\s*)${pattern};
   // With this:
   const finalPattern = ^([ \t]*)${pattern};

Metadata

Metadata

Assignees

No one assigned

    Labels

    area/coreIssues related to User Interface, OS Support, Core Functionalitystatus/need-triageIssues that need to be triaged by the triage automation.

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions