{"record":{"id":"2236ad27463d0eb0","repo":"windmill-labs/windmill","slug":"old-string-matched-matchcount-locations-make-i","errorCode":null,"errorMessage":"old_string matched ${matchCount} locations. Make it more specific or set replace_all to true.","messagePattern":"old_string matched (.+?) locations\\. Make it more specific or set replace_all to true\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"frontend/src/lib/components/copilot/chat/shared.ts","lineNumber":311,"sourceCode":"\n/**\n * Match-count-validated exact text replacement. Throws when `oldString` is\n * missing, and (unless `replaceAll`) when it appears more than once.\n * `contextLabel` flows into the error message (\"not found in the <label>.\").\n */\nexport function findAndReplace(\n\tcontent: string,\n\toldString: string,\n\tnewString: string,\n\treplaceAll: boolean,\n\tcontextLabel: string\n): string {\n\tconst matchCount = countExactMatches(content, oldString)\n\tif (matchCount === 0) {\n\t\tthrow new Error(`old_string was not found in the ${contextLabel}.`)\n\t}\n\tif (!replaceAll && matchCount !== 1) {\n\t\tthrow new Error(\n\t\t\t`old_string matched ${matchCount} locations. Make it more specific or set replace_all to true.`\n\t\t)\n\t}\n\treturn applyExactReplace(content, oldString, newString, replaceAll)\n}\n\nexport const extractAllModules = (modules: FlowModule[]): FlowModule[] => {\n\treturn modules.flatMap((m) => {\n\t\tif (m.value.type === 'forloopflow' || m.value.type === 'whileloopflow') {\n\t\t\treturn [m, ...extractAllModules(m.value.modules)]\n\t\t}\n\t\tif (m.value.type === 'branchall') {\n\t\t\treturn [m, ...extractAllModules(m.value.branches.flatMap((b) => b.modules))]\n\t\t}\n\t\tif (m.value.type === 'branchone') {\n\t\t\treturn [\n\t\t\t\tm,\n\t\t\t\t...extractAllModules([...m.value.branches.flatMap((b) => b.modules), ...m.value.default])","sourceCodeStart":293,"sourceCodeEnd":329,"githubUrl":"https://github.com/windmill-labs/windmill/blob/e474e8803ce2ff5c2df09a58dab51d45f5c922ca/frontend/src/lib/components/copilot/chat/shared.ts#L293-L329","documentation":"findAndReplace requires old_string to be unique unless replaceAll is set. When the literal old_string occurs more than once and replaceAll is false, it throws this error with the exact match count so the caller (usually the model) can disambiguate. This guards against unintended edits at multiple locations.","triggerScenarios":"An edit tool passed a short or generic old_string (e.g. '});', a repeated variable assignment, or a common import line) that matches N>1 places, with replace_all=false. countExactMatches found multiple occurrences in the file or flow JSON.","commonSituations":"Model tries to edit a common pattern like a closing brace, a repeated import, or 'return null' that exists in several functions; user asks to change one occurrence of a duplicated block; generated code contains two identical placeholder lines.","solutions":["Include more surrounding lines in old_string so it matches exactly one location, then retry.","If all occurrences should change, set replace_all: true on the tool call.","Replace a larger unique block that contains the target line as one edit.","Split the edit into multiple calls, each with context that makes the target occurrence unique."],"exampleFix":"// before\nfindAndReplace(content, \"return null\", \"return undefined\", false, \"the file\") // 3 matches\n// after\nfindAndReplace(content, \"if (!user) {\\n\\treturn null\\n}\", \"if (!user) {\\n\\treturn undefined\\n}\", false, \"the file\")","handlingStrategy":"validation","validationCode":"const count = content.split(oldString).length - 1\nif (!replaceAll && count > 1) {\n  throw new Error(`old_string matches ${count} places; add surrounding context or set replace_all`)\n}","typeGuard":null,"tryCatchPattern":"try {\n  return findAndReplace(content, oldString, newString, replaceAll, 'the file')\n} catch (e) {\n  const m = e.message.match(/matched (\\d+) locations/)\n  if (m) {\n    const widened = widenWithSurroundingContext(content, oldString, 3)\n    return findAndReplace(content, widened, replaceSnippet(newString), false, 'the file')\n  }\n  throw e\n}","preventionTips":["Make old_string long enough to be unique (include enclosing function or block lines).","Count occurrences with content.split(oldString).length - 1 before calling.","Set replace_all: true deliberately when every occurrence should change.","Avoid editing generic patterns like bare braces or repeated imports without context."],"tags":["string-replacement","edit-tool","ambiguity"],"backgroundTag":"ambiguous-match","analyzedSha":"e474e8803ce2ff5c2df09a58dab51d45f5c922ca","analyzedAt":"2026-09-03T12:38:19.024Z","contentChangedAt":"2026-09-03T12:38:19.024Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}