can1357/oh-my-pi · error · NotFoundError

File not found: ${relativePath}

Error message

File not found: ${relativePath}

What it means

HashlineFilesystem.readText() resolves the relative path against the session cwd and reads via readEditFileText; ENOENT — and notably also a readEditFileText failure whose message is literally 'File not found: <path>' — is converted to a NotFoundError. This second branch exists because readEditFileText may throw its own 'File not found' Error for cases isEnoent misses (e.g. path inside a non-existent directory reported by the edit-file reader).

Source

Thrown at packages/coding-agent/src/edit/hashline/filesystem.ts:118

		// snapshot tag uniquely names. Confine the redirect to locations a plain
		// "write" may legitimately target:
		//  1. the working tree (the model dropped the directory), or
		//  2. the session `local://` sandbox where plan/scratch artifacts live —
		//     the snapshot tag proves the model wrote/read that exact file this
		//     session, so a bare `plan.md#tag` should land on `local://plan.md`.
		// The secret vault and any other out-of-tree path stay refused.
		const root = canonicalSnapshotKey(this.session.cwd);
		if (resolvedPath === root || resolvedPath.startsWith(`${root}${path.sep}`)) return true;
		return targetsLocalSandbox(this.session, resolvedPath);
	}

	async readText(relativePath: string): Promise<string> {
		const absolutePath = this.resolveAbsolute(relativePath);
		let content: string;
		try {
			content = await readEditFileText(absolutePath, relativePath);
		} catch (error) {
			if (isEnoent(error)) throw new NotFoundError(relativePath, error);
			if (error instanceof Error && error.message === `File not found: ${relativePath}`) {
				throw new NotFoundError(relativePath, error);
			}
			throw error;
		}
		// Refuse edits against generated files (lockfiles, models.json, …).
		assertEditableFileContent(content, relativePath, this.session.settings);
		return content;
	}

	override async readBinary(relativePath: string): Promise<Uint8Array | undefined> {
		const absolutePath = this.resolveAbsolute(relativePath);
		if (isNotebookPath(absolutePath)) return undefined;
		try {
			return await fs.readFile(absolutePath);
		} catch (error) {
			if (isEnoent(error)) throw new NotFoundError(relativePath, error);
			throw error;

View on GitHub (pinned to 9690622007)

Solutions

  1. Verify the file exists relative to the session cwd (ls) and correct the relative path.
  2. Re-read the file listing / re-run read to refresh which files exist, then re-issue.
  3. If the file should exist but was deleted, restore it or use the write tool to recreate it.

Example fix

// before
edit({ section: `@@@ src/helpers/removed.ts:a1b2\n- x` });
// after
// file was deleted in rebase — target the new location
edit({ section: `@@@ src/utils/helpers.ts:c3d4\n- x\n+ y` });
Defensive patterns

Strategy: try-catch

Validate before calling

import * as path from 'node:path';
const abs = path.resolve(session.cwd, relativePath);
if (!(await Bun.file(abs).exists())) {
  throw new Error(`Refusing edit: ${relativePath} does not exist under ${session.cwd}`);
}

Try / catch

import { NotFoundError } from '.../filesystem';
try {
  const text = await fs.readText(relativePath);
  return applyEdit(text);
} catch (e) {
  if (e instanceof NotFoundError) {
    return { status: 'missing-file', path: relativePath }; // skip or recreate via write
  }
  throw e;
}

Prevention

When it happens

Trigger: Applying an edit or preview against a relativePath that does not exist under session.cwd: file deleted, wrong relative path, path never created, or edit issued against a file in a removed directory.

Common situations: Stale session state after a git checkout/rebase removed the file; model inventing a path not in its context; case-sensitivity mismatch (Foo.ts vs foo.ts) on Linux.

Understand the failure class

Background: "File not found" and ENOENT errors: why libraries can't find a file that should exist — this error's family across 50 libraries.

Related errors


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