ultraworkers/claw-code · error · std::io::Error
old_string and new_string must differ
Error message
old_string and new_string must differ
What it means
`edit_file` (runtime/src/file_ops.rs:277) rejects edits where `old_string == new_string` by exact byte equality, before it even checks that old_string exists. This is a no-op guard: a replacement that changes nothing is almost always a caller bug. `ErrorKind::InvalidInput`.
Source
Thrown at rust/crates/runtime/src/file_ops.rs:277
file_path: absolute_path.to_string_lossy().into_owned(),
content: content.to_owned(),
structured_patch: make_patch(original_file.as_deref().unwrap_or(""), content),
original_file,
git_diff: None,
})
}
/// 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)?;
View on GitHub (pinned to 08106b0c37)
Solutions
- Skip the edit when the strings are equal instead of calling edit_file.
- If you intended a change, inspect both strings — invisible differences (or their absence) mean you pasted the same text twice.
- For wholesale rewrites with identical intent, use write_file only when content actually differs.
Example fix
// before
edit_file(path, &old, &new, false)?; // old_string and new_string must differ
// after
if old != new {
edit_file(path, &old, &new, false)?;
} Defensive patterns
Strategy: validation
Validate before calling
if old_string != new_string {
let out = edit_file(path, old_string, new_string, replace_all)?;
} else {
// no-op edit: skip (or treat as idempotent success)
} Try / catch
if let Err(e) = edit_file(path, old, new, false) {
if e.kind() == std::io::ErrorKind::InvalidInput
&& e.to_string().contains("must differ") { /* skip as no-op */ } else { return Err(e); }
} Prevention
- Compare old/new byte-for-byte before calling edit_file
- In templating code, skip substitution when value equals placeholder
- Don't use edit_file to 'touch' a file — it is rejected as a no-op
When it happens
Trigger: Templating code that substitutes a placeholder with a value that turns out equal to the placeholder; retrying an already-applied edit with the same strings; a copy-paste mistake putting the same snippet in both fields.
Common situations: Agent loops computing new_string from old_string and hitting the identity case; idempotent re-run of a patch script that does not track whether it already applied.
Related errors
- old_string not found in file
- skill source '{}' must be a directory with SKILL.md or a mar
- unable to derive an installable invocation name from '{}'
- file is too large ({} bytes, max {} bytes)
- file appears to be binary
AI-assisted analysis of ultraworkers/claw-code@08106b0c37 (2026-08-18).
Data as JSON: /api/errors/5b38e0f5b62756e5.
Report an issue: GitHub.