can1357/oh-my-pi · error · ApplyPatchError

File not found: ${path}

Error message

File not found: ${path}

What it means

The patch machinery tried to read the existing file that the patch targets (readExistingPatchFile) and got an ENOENT (or a wrapped 'File not found') from the FileSystem layer. It rethrows as an ApplyPatchError so callers can treat it as a patch-argument problem rather than an unexpected crash. This means the diff cannot be applied because the target file does not exist on disk at the resolved absolute path.

Source

Thrown at packages/coding-agent/src/edit/modes/patch.ts:1044

	// Apply the replacement
	const before = normalizedContent.substring(0, matchOutcome.match.startIndex);
	const after = normalizedContent.substring(matchOutcome.match.startIndex + matchOutcome.match.actualText.length);
	return { content: before + adjustedNewText + after, warnings };
}

function applyTrailingNewlinePolicy(content: string, hadFinalNewline: boolean): string {
	if (hadFinalNewline) {
		return content.endsWith("\n") ? content : `${content}\n`;
	}
	return content.replace(/\n+$/u, "");
}

async function readExistingPatchFile(fileSystem: FileSystem, absolutePath: string, path: string): Promise<string> {
	try {
		return await fileSystem.read(absolutePath);
	} catch (error) {
		if (isEnoent(error) || (error instanceof Error && error.message.startsWith("File not found:"))) {
			throw new ApplyPatchError(`File not found: ${path}`);
		}
		throw error;
	}
}

/**
 * A prefix/substring strategy matched pattern lines that cover only part of
 * the corresponding file lines; replacing whole lines would silently drop the
 * uncovered text the model never saw. Allow the replacement only when every
 * discarded piece (normalized) survives somewhere in the hunk's new lines.
 */
function assertPartialMatchPreservesDiscardedText(
	path: string,
	pattern: string[],
	matchedLines: string[],
	newLines: string[],
	matchStartIndex: number,
): void {

View on GitHub (pinned to 9690622007)

Solutions

  1. Verify the file exists at the given path (ls / read the file) and correct the path in the patch header.
  2. If the file should be new, change the patch header to *** Add File (or *** Delete File semantics as appropriate) instead of Update.
  3. Re-read the file to get its current path, then regenerate the patch.
  4. Check the working directory root the patch is applied against; relative paths resolve from there.

Example fix

// before
*** Begin Patch
*** Update File: src/servcie.ts
@@
-old
+new
*** End Patch

// after
*** Begin Patch
*** Update File: src/service.ts
@@
-old
+new
*** End Patch
Defensive patterns

Strategy: validation

Validate before calling

import * as fs from 'node:fs/promises';
const target = resolve(workspaceRoot, patchPath);
try {
  await fs.access(target);
} catch {
  throw new Error(`Patch target does not exist: ${patchPath}. Use Add File for new files.`);
}

Try / catch

try {
  applyPatch(patch);
} catch (err) {
  if (err instanceof ApplyPatchError && err.message.startsWith('File not found:')) {
    // prompt user to verify path or switch to Add File
  } else throw err;
}

Prevention

When it happens

Trigger: Calling the apply-patch/edit tool with a patch whose *** Update File header names a file that is not present in the workspace, or whose relative path resolves against the wrong working directory; also when the FileSystem.read implementation wraps ENOENT in a generic Error with a message starting 'File not found:'.

Common situations: LLM-generated patches that hallucinate a file path or use a path from a different repo layout; the file was deleted or renamed before the patch ran; the patch references an absolute path from another machine; a patch meant to *** Add File was emitted as *** Update File.

Related errors


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/8cfbb2d8a0a7fdd4. Report an issue: GitHub.