ultraworkers/claw-code · error · std::io::Error
old_string not found in file
Error message
old_string not found in file
What it means
`edit_file` (runtime/src/file_ops.rs:283) requires the file's current contents to contain `old_string` as an exact byte substring (no whitespace normalization) and returns `ErrorKind::NotFound` otherwise. Note this port has NO multiple-match ambiguity error: without `replace_all` it replaces the FIRST occurrence via `replacen(.., 1)`.
Source
Thrown at rust/crates/runtime/src/file_ops.rs:283
}
/// Performs an in-file string replacement and returns patch metadata.
pub fn edit_file(
path: &str,
old_string: &str,
new_string: &str,
replace_all: bool,
) -> io::Result<EditFileOutput> {
let absolute_path = normalize_path(path)?;
let original_file = fs::read_to_string(&absolute_path)?;
if old_string == new_string {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"old_string and new_string must differ",
));
}
if !original_file.contains(old_string) {
return Err(io::Error::new(
io::ErrorKind::NotFound,
"old_string not found in file",
));
}
let updated = if replace_all {
original_file.replace(old_string, new_string)
} else {
original_file.replacen(old_string, new_string, 1)
};
fs::write(&absolute_path, &updated)?;
Ok(EditFileOutput {
file_path: absolute_path.to_string_lossy().into_owned(),
old_string: old_string.to_owned(),
new_string: new_string.to_owned(),
original_file: original_file.clone(),
structured_patch: make_patch(&original_file, &updated),View on GitHub (pinned to 08106b0c37)
Solutions
- Re-read the file now and copy old_string verbatim from the fresh content.
- Widen old_string with surrounding unique context lines so it matches exactly.
- Pre-check with the same substring test before calling, and re-verify tabs/CRLF with `cat -A file | head`.
Example fix
# before (stale copy: file changed since read)
Edit(file="src/lib.rs", old_string="fn main() {\n println!(\"hi\")", ...)
# after (re-read, then anchor on current exact bytes)
Read(file="src/lib.rs")
Edit(file="src/lib.rs", old_string="fn main() {\n println!(\"hello\")", ...) Defensive patterns
Strategy: validation
Validate before calling
let current = std::fs::read_to_string(path)?;
if !current.contains(old_string) {
// re-copy old_string from `current` (exact bytes, no normalization) before editing
} Try / catch
match edit_file(path, old, new, replace_all) {
Err(e) if e.kind() == std::io::ErrorKind::NotFound
&& e.to_string().contains("old_string not found") => { /* re-read file, rebuild old_string from fresh bytes */ }
other => other,
} Prevention
- Always re-read the file immediately before editing; stale snapshots are the top cause
- Copy old_string verbatim including tabs/trailing spaces/CRLF
- Anchor with surrounding context lines to make matches unambiguous
- This port replaces the FIRST match by default — use replace_all only when intended
When it happens
Trigger: The file changed between the read that produced old_string and the edit (stale buffer); tabs vs spaces; trailing whitespace or line-ending differences; copied old_string from rendered output that collapsed spacing; wrong file path (edited a different copy).
Common situations: Concurrent formatters/linters rewriting the file mid-session; agent editing from an outdated file snapshot; CRLF files edited with LF-anchored strings; monorepo duplicate paths.
Related errors
- old_string and new_string must differ
- skill source '{}' not found: {e}
- file is too large ({} bytes, max {} bytes)
- file appears to be binary
- content is too large ({} bytes, max {} bytes)
AI-assisted analysis of ultraworkers/claw-code@08106b0c37 (2026-08-18).
Data as JSON: /api/errors/ed06118c8bedc049.
Report an issue: GitHub.