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

  1. Re-read the current file/flow content and copy old_string verbatim from it, then retry the edit.
  2. Shrink or expand old_string to include enough unique surrounding context exactly as it appears now.
  3. If the model keeps failing, have it rewrite the whole file/JSON instead of doing a surgical replace.
  4. 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

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.