{"record":{"id":"5b38e0f5b62756e5","repo":"ultraworkers/claw-code","slug":"old-string-and-new-string-must-differ","errorCode":null,"errorMessage":"old_string and new_string must differ","messagePattern":"old_string and new_string must differ","errorType":"validation","errorClass":"std::io::Error","httpStatus":null,"severity":"error","filePath":"rust/crates/runtime/src/file_ops.rs","lineNumber":277,"sourceCode":"        file_path: absolute_path.to_string_lossy().into_owned(),\n        content: content.to_owned(),\n        structured_patch: make_patch(original_file.as_deref().unwrap_or(\"\"), content),\n        original_file,\n        git_diff: None,\n    })\n}\n\n/// Performs an in-file string replacement and returns patch metadata.\npub fn edit_file(\n    path: &str,\n    old_string: &str,\n    new_string: &str,\n    replace_all: bool,\n) -> io::Result<EditFileOutput> {\n    let absolute_path = normalize_path(path)?;\n    let original_file = fs::read_to_string(&absolute_path)?;\n    if old_string == new_string {\n        return Err(io::Error::new(\n            io::ErrorKind::InvalidInput,\n            \"old_string and new_string must differ\",\n        ));\n    }\n    if !original_file.contains(old_string) {\n        return Err(io::Error::new(\n            io::ErrorKind::NotFound,\n            \"old_string not found in file\",\n        ));\n    }\n\n    let updated = if replace_all {\n        original_file.replace(old_string, new_string)\n    } else {\n        original_file.replacen(old_string, new_string, 1)\n    };\n    fs::write(&absolute_path, &updated)?;\n","sourceCodeStart":259,"sourceCodeEnd":295,"githubUrl":"https://github.com/ultraworkers/claw-code/blob/08106b0c3771ef5b4a5aa176acccd460e88b7325/rust/crates/runtime/src/file_ops.rs#L259-L295","documentation":"`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`.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nedit_file(path, &old, &new, false)?;   // old_string and new_string must differ\n\n// after\nif old != new {\n    edit_file(path, &old, &new, false)?;\n}","handlingStrategy":"validation","validationCode":"if old_string != new_string {\n    let out = edit_file(path, old_string, new_string, replace_all)?;\n} else {\n    // no-op edit: skip (or treat as idempotent success)\n}","typeGuard":null,"tryCatchPattern":"if let Err(e) = edit_file(path, old, new, false) {\n    if e.kind() == std::io::ErrorKind::InvalidInput\n        && e.to_string().contains(\"must differ\") { /* skip as no-op */ } else { return Err(e); }\n}","preventionTips":["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"],"tags":["file-ops","edit","invalid-input"],"backgroundTag":"no-op-edit-rejected","analyzedSha":"08106b0c3771ef5b4a5aa176acccd460e88b7325","analyzedAt":"2026-08-18T00:29:38.590Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}