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):
- The regex captures everything from the start of a line matching the pattern.
- 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."
- 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};
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:
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):
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.