{"record":{"id":"e82bde49eef264b7","repo":"CherryHQ/cherry-studio","slug":"old-string-and-new-string-must-be-different","errorCode":null,"errorMessage":"old_string and new_string must be different","messagePattern":"old_string and new_string must be different","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/main/ai/mcp/servers/filesystem/types.ts","lineNumber":525,"sourceCode":"  WhitespaceNormalizedReplacer,\n  IndentationFlexibleReplacer,\n  EscapeNormalizedReplacer,\n  TrimmedBoundaryReplacer,\n  ContextAwareReplacer,\n  MultiOccurrenceReplacer\n]\n\n/**\n * Replace oldString with newString in content using fuzzy matching\n */\nexport function replaceWithFuzzyMatch(\n  content: string,\n  oldString: string,\n  newString: string,\n  replaceAll = false\n): string {\n  if (oldString === newString) {\n    throw new Error('old_string and new_string must be different')\n  }\n\n  let notFound = true\n\n  for (const replacer of ALL_REPLACERS) {\n    for (const search of replacer(content, oldString)) {\n      const index = content.indexOf(search)\n      if (index === -1) continue\n      notFound = false\n      if (replaceAll) {\n        return content.replaceAll(search, newString)\n      }\n      const lastIndex = content.lastIndexOf(search)\n      if (index !== lastIndex) continue\n      return content.substring(0, index) + newString + content.substring(index + search.length)\n    }\n  }\n","sourceCodeStart":507,"sourceCodeEnd":543,"githubUrl":"https://github.com/CherryHQ/cherry-studio/blob/726446b54cd69ffe51a276638672f6d95ca0768c/src/main/ai/mcp/servers/filesystem/types.ts#L507-L543","documentation":"Thrown by replaceWithFuzzyMatch (types.ts:518) when oldString === newString. The function performs an edit, so an identical old/new pair is a no-op and is rejected upfront as a caller logic error before any replacer runs. This guards the edit tool against accidental no-op edits that would otherwise silently succeed.","triggerScenarios":"Calling the edit tool (or replaceWithFuzzyMatch directly) with old_string and new_string that are byte-identical. Common when both are templated from the same source, copy-pasted, or when the caller intends only a whitespace change but normalizes both sides identically first.","commonSituations":"Caller copies the selection into both old and new fields then forgets to edit the new field; a refactor script derives new from old with no transformation; trailing-newline normalization makes both strings equal; an LLM agent emits the same block twice.","solutions":["Make new_string actually differ from old_string (even a single character change is enough).","If you only want to verify content exists, use a search/read tool instead of edit.","Add an assertion old !== new in the caller before invoking edit to fail fast with your own message."],"exampleFix":"// before\nreplaceWithFuzzyMatch(content, 'const x = 1', 'const x = 1') // throws: old_string and new_string must be different\n\n// after\nreplaceWithFuzzyMatch(content, 'const x = 1', 'const x = 2')","handlingStrategy":"validation","validationCode":"function assertDifferentEdit(oldString: string, newString: string): void {\n  if (oldString === newString) {\n    throw new Error('Refusing no-op edit: old_string equals new_string')\n  }\n}","typeGuard":"function isMeaningfulEdit(oldString: string, newString: string): boolean {\n  return oldString !== newString\n}","tryCatchPattern":"try {\n  replaceWithFuzzyMatch(content, oldString, newString)\n} catch (e) {\n  if (e instanceof Error && e.message === 'old_string and new_string must be different') {\n    // nothing to do; skip the edit entirely\n  } else throw e\n}","preventionTips":["Construct new_string by transforming old_string, then assert they differ before sending.","For verification-only flows, use read/search rather than edit.","Beware auto-normalizers (trim, line-ending) that can equalize both sides."],"tags":["filesystem","validation","mcp","edit-tool","fuzzy-match"],"backgroundTag":null,"analyzedSha":"726446b54cd69ffe51a276638672f6d95ca0768c","analyzedAt":"2026-08-12T17:30:37.448Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}