{"record":{"id":"e9b03918c5241434","repo":"windmill-labs/windmill","slug":"old-string-was-not-found-in-the-contextlabel","errorCode":null,"errorMessage":"old_string was not found in the ${contextLabel}.","messagePattern":"old_string was not found in the (.+?)\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"frontend/src/lib/components/copilot/chat/shared.ts","lineNumber":308,"sourceCode":"\tif (index === -1) return content\n\treturn content.slice(0, index) + newString + content.slice(index + oldString.length)\n}\n\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') {","sourceCodeStart":290,"sourceCodeEnd":326,"githubUrl":"https://github.com/windmill-labs/windmill/blob/e474e8803ce2ff5c2df09a58dab51d45f5c922ca/frontend/src/lib/components/copilot/chat/shared.ts#L290-L326","documentation":"findAndReplace performs exact literal replacement in a content string (file content or flow JSON) and requires old_string to appear at least once. When countExactMatches returns 0 the function throws this error naming the context (e.g. 'the file' or 'the flow JSON'). This is the Windmill copilot's equivalent of the classic edit-tool 'old_string not found' failure.","triggerScenarios":"Any of the edit tools (updatedContent, updatedFlowJson, globalTools, updated, updatedJson, remarkWindmillPaths) called findAndReplace with an old_string that has zero exact, literal matches in the current content — whitespace, indentation, quoting, or escape differences count as no match.","commonSituations":"The model wrote old_string from memory of an earlier version of the file; the file was reformatted (prettier) after the model read it; the model paraphrased or normalized whitespace; non-exact smart quotes or unicode differences; the edit targets a stale flow JSON after a prior edit already changed the text.","solutions":["Re-read the current file/flow content and copy old_string verbatim from it, then retry the edit.","Shrink or expand old_string to include enough unique surrounding context exactly as it appears now.","If the model keeps failing, have it rewrite the whole file/JSON instead of doing a surgical replace.","Check for invisible differences (tabs vs spaces, trailing whitespace, CRLF vs LF) between old_string and the file."],"exampleFix":"// before (stale snippet)\nfindAndReplace(content, \"const x = 1\", \"const x = 2\", false, \"the file\")\n// after (read fresh content first)\nconst fresh = editor.getValue()\nconst snippet = fresh.match(/const x = .+/)[0]\nfindAndReplace(fresh, snippet, \"const x = 2\", false, \"the file\")","handlingStrategy":"validation","validationCode":"if (typeof oldString !== 'string' || oldString.length === 0) throw new Error('old_string required')\nconst count = content.split(oldString).length - 1\nif (count === 0) throw new Error('old_string does not appear verbatim in current content — re-read the file first')","typeGuard":null,"tryCatchPattern":"try {\n  return findAndReplace(content, oldString, newString, replaceAll, 'the file')\n} catch (e) {\n  if (e instanceof Error && e.message.includes('old_string was not found')) {\n    const fresh = await reloadContent()\n    return findAndReplace(fresh, extractSnippetFrom(fresh, oldString), newString, replaceAll, 'the file')\n  }\n  throw e\n}","preventionTips":["Copy old_string verbatim from the current content, never from memory or an earlier read.","Run a prettier/format pass before generating edits so formatting matches.","Normalize tabs/spaces and line endings between old_string and the file.","Prefer whole-file rewrite when the target text is uncertain."],"tags":["string-replacement","edit-tool","exact-match"],"backgroundTag":"old-string-not-found","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"}