can1357/oh-my-pi · error

File not found: ${targetPath}

Error message

File not found: ${targetPath}

What it means

After computing the target path (either the skill's SKILL.md file, the skill's baseDir for path-only resolution, or a contained resolved path), the handler calls `fs.stat(targetPath)`. If stat fails with ENOENT the target does not exist, and the handler converts the raw ENOENT into this clearer 'File not found' error. Any other stat error (permissions, I/O) is rethrown unchanged.

Source

Thrown at packages/coding-agent/src/internal-urls/skill-protocol.ts:102

				const contained = await resolveContainedPath(skill.containRoot, resolvedPath);
				if (contained.status === "outside") {
					throw new Error(`skill:// path resolves outside the plugin root: ${url.href}`);
				}
				if (contained.status === "missing") {
					throw new Error(`File not found: ${resolvedPath}`);
				}
				targetPath = contained.realPath;
			}
		} else {
			targetPath = context?.pathOnly === true ? skill.baseDir : skill.filePath;
		}

		let stats: fsTypes.Stats;
		try {
			stats = await fs.stat(targetPath);
		} catch (error) {
			if (isEnoent(error)) {
				throw new Error(`File not found: ${targetPath}`);
			}
			throw error;
		}

		if (stats.isDirectory()) {
			return buildDirectoryResource(url.href, targetPath);
		}
		if (!stats.isFile()) {
			throw new Error(`skill:// URL must resolve to a file or directory: ${url.href}`);
		}

		const content = await Bun.file(targetPath).text();
		return {
			url: url.href,
			content,
			contentType: getContentType(targetPath),
			size: Buffer.byteLength(content, "utf-8"),
			sourcePath: targetPath,

View on GitHub (pinned to 9690622007)

Solutions

  1. Stat the path from the error message directly on disk to confirm it is missing.
  2. Re-install or re-sync the skill so its SKILL.md/baseDir exists (remove stale skill cache entries).
  3. Fix the path in the skill:// URL — check spelling and the skill's actual directory layout.
  4. If you control skill registration, ensure `filePath`/`baseDir` point at real paths at registration time.

Example fix

// before
const res = await handler.resolve(parseInternalUrl("skill://old-skill")); // SKILL.md deleted
// after
const res = await handler.resolve(parseInternalUrl("skill://new-skill")); // resynced skill
Defensive patterns

Strategy: try-catch

Validate before calling

import * as fs from "node:fs/promises";
try {
  await fs.stat(targetPath); // SKILL.md, baseDir, or joined path
} catch {
  throw new Error(`skill target missing on disk: ${targetPath}`);
}

Try / catch

try {
  const resource = await handler.resolve(url);
} catch (err) {
  if (err instanceof Error && err.message.startsWith("File not found:")) {
    // resync skills, then retry once
    await resyncSkills();
    return handler.resolve(url);
  }
  throw err;
}

Prevention

When it happens

Trigger: `SkillProtocolHandler.resolve()` where: (a) a `skill://<name>` URL is used and the skill's `filePath` (its SKILL.md) does not exist on disk; (b) a `skill://<name>/<path>` URL is used without a `containRoot` and `path.join(skill.baseDir, relativePath)` does not exist; (c) `context.pathOnly === true` and the skill's `baseDir` directory was deleted.

Common situations: Skill discovered from a config/marketplace but its SKILL.md was never written or was deleted; skills loaded from a stale cache pointing at removed directories; mistyped paths in skill:// URLs for non-plugin skills; a partially-synced skills directory.

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/dd24700ed492d7f2. Report an issue: GitHub.