`edit_file` silently edits the first match when `oldText` occurs more than once
Nobody has claimed this yet.
- Dominant language
- Go
- Stars
- 3.3k
- Forks
- 462
- Avg merge
- 1d 10h
- Merged PRs (30d)
- 273
Description
Description
handleEditFile checks only whether oldText is present, then replaces a single occurrence:
// pkg/tools/builtin/filesystem/filesystem.go:1019-1025
for i, edit := range args.Edits {
if !strings.Contains(modifiedContent, edit.OldText) {
return tools.ResultError(fmt.Sprintf("Edit %d failed: old text not found", i+1)), nil
}
modifiedContent = strings.Replace(modifiedContent, edit.OldText, edit.NewText, 1)
changes = append(changes, fmt.Sprintf("Edit %d: Replaced %d characters", i+1, len(edit.OldText)))
}
There is a guard for zero matches but none for multiple. With N > 1 matches the first is
rewritten, the rest are left alone, and the reported message —
Replaced %d characters — mentions neither the count nor which occurrence was touched. It is
indistinguishable from an unambiguous edit.
Expected Behavior
An oldText that matches more than once is refused, so the caller disambiguates by supplying
more surrounding context. The file is left unmodified.
Actual Behavior
The first occurrence is rewritten and the tool reports plain success.
Steps to Reproduce
conf.py:
def dev():
debug = True
def prod():
debug = True
{"path":"conf.py","edits":[{"oldText":" debug = True","newText":" debug = False"}]}
occurrences of oldText in file = 2
tool reported: "File edited successfully. Replaced 16 characters"
file after:
def dev():
debug = False <- changed
def prod():
debug = True <- silently left alone
Docker Agent version
No response
OS & terminal
No response
Model used
No response
Error output
Screenshots
No response
Additional context
Impact
The model cannot tell whether it edited the site it intended. When it meant the second
occurrence, the wrong code is now modified and the agent has been told it succeeded — so it
does not re-read the file to verify, and the mistake propagates.
The shape in the reproduction (the same assignment in a dev branch and a prod branch) is common
in configuration and environment-branching code, which makes the wrong-site edit both easy to
trigger and hard to trace afterwards.
Refusing an ambiguous match is the established behaviour for this class of tool: the caller is
expected to include enough context to identify a unique site.
Additional context
- Occurrences must be counted against the running content rather than the original file: a
multi-edit call can legitimately have an earlier edit remove one of the duplicates, leaving a
later edit unambiguous. Counting against the original would break that case. strings.Countfolds in the existing zero-match check, so thestrings.Containscall is not
needed alongside it — one scan instead of two.- This is a behaviour change for any caller relying on first-match-wins. Nothing in the Go tests,
e2e/or the recorded cassettes does: the cassettes contain only theedit_fileJSON schema
sent to the model, not multi-occurrence edit calls. - Related, already fixed separately: an empty
oldTextalso passed the presence check and
silently prepended to the file.
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in pkg/tools/builtin/filesystem/filesystem.go at handleEditFile, especially the edit loop around lines 1019-1025. Review the existing Go tests and the issue's notes about running content and multi-edit calls. Done means ambiguous matches are refused without modifying the file, while unique sequential edits continue to work.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- go
- Domain
- devtools, tooling
- Issue type
- Bug
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 78/100