can1357/oh-my-pi · error

File not found: ${resolvedPath}

Error message

File not found: ${resolvedPath}

What it means

This error comes from the skill:// internal-URL protocol handler. When a skill belongs to an Agent Plugin with a containment root (`skill.containRoot`), the requested path is canonically resolved inside that root via `resolveContainedPath`. If the resolution reports `missing`, meaning the path (after resolving symlinks) does not exist within the plugin root, the handler fails closed with 'File not found'.

Source

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

			const relativePath = decodeURIComponent(urlPath.slice(1));
			validateRelativePath(relativePath);
			targetPath = path.join(skill.baseDir, relativePath);

			const resolvedPath = path.resolve(targetPath);
			const resolvedBaseDir = path.resolve(skill.baseDir);
			if (!resolvedPath.startsWith(resolvedBaseDir + path.sep) && resolvedPath !== resolvedBaseDir) {
				throw new Error("Path traversal is not allowed");
			}
			// Agent Plugin skills (§4.1): the resource must canonically resolve
			// within the plugin root; a dangling or unresolvable path fails closed.
			// Symlinks may target other files inside the same package.
			if (skill.containRoot) {
				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()) {

View on GitHub (pinned to 9690622007)

Solutions

  1. Verify the path in the skill:// URL exists inside the plugin root (run `ls` on the plugin's baseDir/containRoot).
  2. Check the plugin version — the file may have been renamed or removed; update the URL or the plugin.
  3. If the file is a symlink, confirm its target resolves inside the plugin root and actually exists.
  4. Read the SKILL.md itself (skill://<name> with no path) to discover the correct bundled file names.

Example fix

// before
const res = await handler.resolve(parseInternalUrl("skill://deploy/scripts/run.sh"));
// after
const res = await handler.resolve(parseInternalUrl("skill://deploy/scripts/run-remote.sh")); // path corrected to match bundled file
Defensive patterns

Strategy: validation

Validate before calling

import * as fs from "node:fs/promises";
import * as path from "node:path";
// Before resolving, confirm the relative path exists under the skill's baseDir.
const rel = "scripts/run-remote.sh";
const abs = path.resolve(skill.baseDir, rel);
try {
  await fs.stat(abs); // throws ENOENT if missing
} catch {
  throw new Error(`Refusing skill:// URL: ${rel} does not exist in skill ${skill.name}`);
}

Try / catch

try {
  const resource = await handler.resolve(url);
} catch (err) {
  if (err instanceof Error && err.message.startsWith("File not found:")) {
    // fall back to reading the SKILL.md index
    return handler.resolve(parseInternalUrl(`skill://${skillName}`));
  }
  throw err;
}

Prevention

When it happens

Trigger: Calling `SkillProtocolHandler.resolve()` on a `skill://<name>/<path>` URL where the skill has a `containRoot` and `resolveContainedPath(skill.containRoot, resolvedPath)` returns status `"missing"` — i.e. the referenced relative path does not exist (or is an unresolvable/dangling symlink) inside the plugin root.

Common situations: Referencing an auxiliary file (script, template, data file) alongside a plugin skill's SKILL.md that was renamed, moved, or never shipped; typos in the path portion of skill:// URLs; dangling symlinks inside the plugin package; tooling or an agent hallucinating a path that exists in a different plugin version.

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