{"record":{"id":"d5fd6345a75e245e","repo":"JuliusBrussee/caveman","slug":"caveman-code-old-string-and-new-string-are-identi","errorCode":null,"errorMessage":"caveman-code: old_string and new_string are identical","messagePattern":"caveman-code: old_string and new_string are identical","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"warning","filePath":"packages/agent/src/code.ts","lineNumber":343,"sourceCode":"  });\n\n  const editTool = tool({\n    name: \"edit_file\",\n    description:\n      \"Replace an exact string in a workspace file. The old string must appear exactly \" +\n      \"once unless replace_all is set. Writes to disk.\",\n    input: schema.object({\n      path: schema.string(),\n      old_string: schema.string(),\n      new_string: schema.string(),\n      replace_all: schema.optional(schema.boolean()),\n    }),\n    effect: \"write\",\n    result: \"inline\",\n    timeoutMs: READ_TIMEOUT_MS,\n    async execute(input) {\n      if (input.old_string === input.new_string) {\n        throw new Error(\"caveman-code: old_string and new_string are identical\");\n      }\n      const target = await contained(input.path);\n      const content = await readFile(target, \"utf8\");\n      const occurrences = content.split(input.old_string).length - 1;\n      if (occurrences === 0) {\n        throw new Error(`caveman-code: old_string not found in ${input.path}`);\n      }\n      if (occurrences > 1 && input.replace_all !== true) {\n        throw new Error(\n          `caveman-code: old_string appears ${occurrences} times in ${input.path}; ` +\n          \"add surrounding context or pass replace_all\",\n        );\n      }\n      // split/join UNCONDITIONALLY. String.prototype.replace\n      // interprets `$&`, `$\\``, `$'`, `$$`, `$1`… in the REPLACEMENT even for a\n      // string pattern, so a new_string containing any of them would silently\n      // corrupt the file. The non-replace_all branch is guaranteed exactly one\n      // occurrence above, so joining replaces precisely that one.","sourceCodeStart":325,"sourceCodeEnd":361,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/27d5a3981a347890211bb1bf2439e5c821a63bc9/packages/agent/src/code.ts#L325-L361","documentation":"edit_file refuses a no-op edit: old_string and new_string must differ byte-for-byte. This is an upfront guard so callers notice malformed edit requests (e.g. a template that substitutes the same value into both fields) before any disk write is attempted.","triggerScenarios":"Calling edit_file with identical old_string and new_string values — typically a scripted caller whose before/after variables collapsed to the same content, or a model emitting a placeholder edit.","commonSituations":"Code-generation loops producing degenerate edits; templating where the substitution failed and both placeholders rendered identically; refactoring scripts that computed new content equal to existing content.","solutions":["Diff your old/new strings before calling edit_file; if they are equal, the edit is unnecessary — skip it","Fix the upstream templating/substitution bug that made the two fields collapse to the same value","If you intended a no-op touch (e.g. mtime), use a different mechanism — edit_file will never allow it"],"exampleFix":"// before\nawait edit_file({ path, old_string: snippet, new_string: snippet });\n\n// after\nif (oldString !== newString) {\n  await edit_file({ path, old_string: oldString, new_string: newString });\n}","handlingStrategy":"validation","validationCode":"function assertEditDiffers(old_string: string, new_string: string): void {\n  if (old_string === new_string) throw new Error(\"edit is a no-op; old_string equals new_string\");\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Diff old/new before calling edit_file and skip identical pairs","In generated edit pipelines, assert the computed replacement actually differs from the anchor"],"tags":["validation","edit","no-op","tool-input"],"backgroundTag":null,"analyzedSha":"27d5a3981a347890211bb1bf2439e5c821a63bc9","analyzedAt":"2026-08-15T09:26:11.751Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}