Local file edits can accidentally rewrite whole files and overwrite existing changes
Open 💬 6 comments Opened Jul 6, 2026 by bryanxtong
What version of Codex CLI is running?
codex-cli 0.142.5
What subscription do you have?
gpt-5.4-mini
Which model were you using?
gpt-5.4-mini
What platform is your computer?
_No response_
What terminal emulator and version are you using (if applicable)?
_No response_
Codex doctor report
What issue are you seeing?
Description:
When making a small, targeted change to an existing source file, the editor sometimes rewrites much more than intended instead of applying a minimal patch. In practice, this can replace or corrupt unrelated
content in the same file and overwrite edits that were already present.
What happens:
- A supposed one-line or small block change turns into a large file diff.
- Existing modifications in the same file can be lost.
- Unrelated content such as comments, spacing, or line endings may be changed.
- The resulting diff becomes hard to review and may introduce accidental regressions.
Expected behavior:
- Only the intended lines or code block should be modified.
- Existing unrelated changes in the file must be preserved.
- If the target context is ambiguous, the editor should stop and ask for confirmation instead of rewriting the file.
Impact:
- High risk of overwriting user work in progress.
- Makes code review unreliable.
- Increases the chance of accidental bugs and unnecessary rollback.
Suggested fix:
- Use true minimal patching for file edits.
- Validate that the target context is unique before applying a change.
- Avoid whole-file rewrite behavior for local edits.
- Verify the resulting diff scope after editing and abort if it is larger than expected.
What steps can reproduce the bug?
• 1. Open an existing source file that already has uncommitted local changes.
- Make a small targeted edit request, such as changing one line inside a method.
- Apply the edit using the current file-edit workflow.
- Inspect git diff for that file.
Observed result
- The diff is much larger than the intended change.
- Unrelated lines, comments, or formatting in the same file may be altered.
- In some cases, previously existing modifications in that file are overwritten or lost.
Expected result
- Only the intended line or code block should change.
- Existing unrelated edits in the same file should be preserved.
What is the expected behavior?
_No response_
Additional information
_No response_
6 Comments
It always replace comments automatically. even edit the whole java file to one line. and a lot of edit errors and result in git restore to rollback change.
<img width="1923" height="1843" alt="Image" src="https://github.com/user-attachments/assets/44011caf-df36-4cc2-bc90-8fc1e5b7e7e6" />
I would treat this as an edit transaction boundary, not just a diff-size bug.
A safe local-edit path should preserve three invariants:
If any invariant fails, the editor should stop before writing or leave the file unchanged and ask for confirmation.
The dangerous case is a fallback from minimal patch to whole-file rewrite without an explicit write boundary. That can turn a small requested change into a destructive operation against unrelated user work.
A useful regression would start with a dirty file, apply a one-line edit, and assert that pre-existing unrelated hunks remain byte-for-byte unchanged.
codex gives me why that happens as below: ( Bryan: the problem is that It alway tries to reuse the same error way of editing, and finally he tried python to make it work.)
The issue was my unsafe edit method: I used brittle replace/write operations instead of minimal patches, and I kept retrying the same failing approach. That caused whole-file rewrites and sometimes encoding/BOM/line-ending corruption on Windows, which could overwrite unrelated changes.
That explanation matches the failure shape I was trying to isolate: the unsafe part is not just that the final diff was too large, but that the edit operation lost its transaction boundary and then kept retrying through the same broad writer.
A useful regression would separate three states:
The important invariant is that a failed local edit cannot widen authority on the next attempt. If the patch cannot prove the target span and write set, the safe result is no effect plus a clear diagnostic, not a full-file rewrite through a fallback path.
The problem is that even I have the rules in AGENTS.md, somtimes It does obey them and sticks to its own bad habits of editing.
<img width="1923" height="1843" alt="Image" src="https://github.com/user-attachments/assets/4c9c829d-ba37-4fa7-83fd-7bcc5e1d3990" />
This looks like an edit-scope support boundary, not only a patching quality issue.
The runtime should separate:
edit request accepted
≠
the resulting file mutation is supported by the requested scope
For a small targeted edit, the required support should include:
Minimal invariant:
AppliedDiffScope ⊆ RequestedEditScope
If the resulting diff modifies unrelated lines or overwrites pre-existing local changes, the edit should be treated as unsupported even if the file write succeeds.
Minimal verifier:
def verify_edit_scope(request, diff):
checks = {
"target_context_found": diff["target_context_found"],
"target_context_unique": diff["target_context_unique"],
"diff_inside_requested_scope": set(diff["changed_ranges"]).issubset(
set(request["allowed_ranges"])
),
"unrelated_local_changes_preserved": diff["unrelated_local_changes_preserved"],
"diff_size_within_limit": diff["changed_line_count"] <= request["max_changed_lines"],
"outside_formatting_unchanged": diff["outside_formatting_unchanged"],
}
failed = [name for name, ok in checks.items() if not ok]
if failed:
return {
"decision": "reject_or_request_confirmation",
"failed_support": failed
}
return {
"decision": "apply_edit",
"failed_support": []
}
Minimal negative controls:
[
{
"case": "small_edit_rewrites_unrelated_lines",
"request": {
"allowed_ranges": ["function_A_lines_20_24"],
"max_changed_lines": 5
},
"diff": {
"target_context_found": true,
"target_context_unique": true,
"changed_ranges": [
"function_A_lines_20_24",
"function_B_lines_80_120"
],
"unrelated_local_changes_preserved": true,
"changed_line_count": 45,
"outside_formatting_unchanged": false
},
"expected": "reject_or_request_confirmation"
},
{
"case": "target_context_ambiguous",
"request": {
"allowed_ranges": ["target_block"]
},
"diff": {
"target_context_found": true,
"target_context_unique": false,
"changed_ranges": ["target_block"],
"unrelated_local_changes_preserved": true,
"changed_line_count": 3,
"outside_formatting_unchanged": true
},
"expected": "reject_or_request_confirmation"
},
{
"case": "preexisting_local_change_overwritten",
"request": {
"allowed_ranges": ["function_A_lines_20_24"]
},
"diff": {
"target_context_found": true,
"target_context_unique": true,
"changed_ranges": ["function_A_lines_20_24"],
"unrelated_local_changes_preserved": false,
"changed_line_count": 4,
"outside_formatting_unchanged": true
},
"expected": "reject_or_request_confirmation"
}
]
Minimal positive control:
{
"case": "bounded_single_block_edit",
"request": {
"allowed_ranges": ["function_A_lines_20_24"],
"max_changed_lines": 5
},
"diff": {
"target_context_found": true,
"target_context_unique": true,
"changed_ranges": ["function_A_lines_20_24"],
"unrelated_local_changes_preserved": true,
"changed_line_count": 3,
"outside_formatting_unchanged": true
},
"expected": "apply_edit"
}
The key boundary is:
file write succeeded
does not imply
the edit respected the requested mutation scope.
If the patch engine cannot prove the target context is unique and the final diff remains inside the requested scope, it should stop and ask for confirmation instead of rewriting a larger part of the file.