windmill-labs/windmill · error
old_string was not found in the ${contextLabel}.
Error message
old_string was not found in the ${contextLabel}. What it means
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.
Source
Thrown at frontend/src/lib/components/copilot/chat/shared.ts:308
if (index === -1) return content
return content.slice(0, index) + newString + content.slice(index + oldString.length)
}
/**
* Match-count-validated exact text replacement. Throws when `oldString` is
* missing, and (unless `replaceAll`) when it appears more than once.
* `contextLabel` flows into the error message ("not found in the <label>.").
*/
export function findAndReplace(
content: string,
oldString: string,
newString: string,
replaceAll: boolean,
contextLabel: string
): string {
const matchCount = countExactMatches(content, oldString)
if (matchCount === 0) {
throw new Error(`old_string was not found in the ${contextLabel}.`)
}
if (!replaceAll && matchCount !== 1) {
throw new Error(
`old_string matched ${matchCount} locations. Make it more specific or set replace_all to true.`
)
}
return applyExactReplace(content, oldString, newString, replaceAll)
}
export const extractAllModules = (modules: FlowModule[]): FlowModule[] => {
return modules.flatMap((m) => {
if (m.value.type === 'forloopflow' || m.value.type === 'whileloopflow') {
return [m, ...extractAllModules(m.value.modules)]
}
if (m.value.type === 'branchall') {
return [m, ...extractAllModules(m.value.branches.flatMap((b) => b.modules))]
}
if (m.value.type === 'branchone') {View on GitHub (pinned to e474e8803c)
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.
Example fix
// before (stale snippet) findAndReplace(content, "const x = 1", "const x = 2", false, "the file") // after (read fresh content first) const fresh = editor.getValue() const snippet = fresh.match(/const x = .+/)[0] findAndReplace(fresh, snippet, "const x = 2", false, "the file")
Defensive patterns
Strategy: validation
Validate before calling
if (typeof oldString !== 'string' || oldString.length === 0) throw new Error('old_string required')
const count = content.split(oldString).length - 1
if (count === 0) throw new Error('old_string does not appear verbatim in current content — re-read the file first') Try / catch
try {
return findAndReplace(content, oldString, newString, replaceAll, 'the file')
} catch (e) {
if (e instanceof Error && e.message.includes('old_string was not found')) {
const fresh = await reloadContent()
return findAndReplace(fresh, extractSnippetFrom(fresh, oldString), newString, replaceAll, 'the file')
}
throw e
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
AI-assisted analysis of windmill-labs/windmill@e474e8803c (2026-09-03).
Data as JSON: /api/errors/e9b03918c5241434.
Report an issue: GitHub.