vercel/ai · error

Text to replace was not found in ${inputPath}

Error message

Text to replace was not found in ${inputPath}

What it means

Thrown by editFile when the file was read successfully but current.indexOf(oldText) returns -1 — the oldText snippet does not appear exactly (byte-for-byte) in the file content. The edit is a plain substring replacement, so any difference in whitespace, casing, escaping, or line endings causes this error.

Source

Thrown at packages/harness-cline/src/cline-remote-ops.ts:241

    inputPath: string,
    oldText: string,
    newText: string,
  ): Promise<void> => {
    const remotePath = resolvePath(inputPath);
    const resolved = assertWorkspacePath(
      await resolveExistingSandboxPath({
        remotePath,
        inputPath,
        missingMessage: `File not found: ${inputPath}`,
      }),
    );
    const current = await sandbox.readTextFile({ path: resolved });
    if (current == null) {
      throw new Error(`File not found: ${inputPath}`);
    }
    const index = current.indexOf(oldText);
    if (index === -1) {
      throw new Error(`Text to replace was not found in ${inputPath}`);
    }
    const updated = `${current.slice(0, index)}${newText}${current.slice(
      index + oldText.length,
    )}`;
    await writeFile(inputPath, updated);
  };

  return {
    resolvePath,
    readFile,
    writeFile,
    editFile,

    async bash(command, input) {
      const controller = new AbortController();
      // `input.timeout` is expressed in seconds (the `bash` tool contract),
      // so convert to milliseconds for `setTimeout`.
      const timeoutId =

View on GitHub (pinned to 69428b1f8b)

Solutions

  1. Re-read the file (ops.readFile) and copy oldText exactly from its current content, preserving whitespace and line endings.
  2. Shorten oldText to a smaller unique substring that is easier to match exactly.
  3. Check for already-applied edits — the target text may already contain newText.
  4. Normalize line endings: if unsure, do the replacement via ops.bash (sed/perl) instead of exact substring matching.

Example fix

// before: snippet from stale memory
await ops.editFile('app.ts', 'const x = 1;', 'const x = 2;'); // Text to replace was not found

// after: read fresh content and match exactly
const current = await ops.readFile('app.ts');
if (!current.includes('const x = 1;')) throw new Error('snippet missing');
await ops.editFile('app.ts', 'const x = 1;', 'const x = 2;');
Defensive patterns

Strategy: validation

Validate before calling

async function assertSnippetPresent(ops, p, snippet) {
  const current = await ops.readFile(p);
  if (!current.includes(snippet)) {
    throw new Error(`oldText not present in ${p}; re-read the file and copy exactly`);
  }
}

Try / catch

try {
  await ops.editFile(path, oldText, newText);
} catch (e) {
  if (e instanceof Error && e.message.startsWith('Text to replace was not found in')) {
    const current = await ops.readFile(path);
    if (current.includes(newText)) return; // already applied
    // retry with a smaller, freshly-copied snippet
    const anchor = oldText.split('\n')[0].trim();
    return ops.editFile(path, anchor, anchor.replace(/old/g, 'new'));
  }
  throw e;
}

Prevention

When it happens

Trigger: editFile where oldText uses different indentation than the file; CRLF vs LF mismatch; the snippet was already replaced in a previous edit; unicode/escape-sequence differences; stale file content in the agent's context after the file changed.

Common situations: LLM agents producing near-miss snippets (tab vs spaces, trimmed trailing whitespace); repeated tool-loop edits applying the same replacement twice; files edited outside the sandbox between the read and the edit; searching across lines with wrong newline style.

Related errors


AI-assisted analysis of vercel/ai@69428b1f8b (2026-08-30). Data as JSON: /api/errors/8dbd3d3b8cf26397. Report an issue: GitHub.