can1357/oh-my-pi · error
local:// URL must resolve to a file or directory: ${url.href
Error message
local:// URL must resolve to a file or directory: ${url.href} What it means
After successful resolution and stat, resolveLocalTarget only accepts directories and regular files. If the target is another node type — FIFO, socket, device file, symlink resolved to something unusual — it throws this error because the resource contract (content reading, sizes) only supports files and directories.
Source
Thrown at packages/coding-agent/src/internal-urls/local-protocol.ts:391
let realTargetPath: string;
try {
realTargetPath = await fs.realpath(targetPath);
} catch (error) {
if (isEnoent(error)) {
throw new Error(`Local file not found: ${url.href}`);
}
throw error;
}
ensureWithinRoot(realTargetPath, resolvedRoot);
const stat = await fs.stat(realTargetPath);
if (stat.isDirectory()) {
return { kind: "directory", path: realTargetPath };
}
if (!stat.isFile()) {
throw new Error(`local:// URL must resolve to a file or directory: ${url.href}`);
}
return { kind: "file", path: realTargetPath, size: stat.size };
}
/**
* Resolve a local:// URL to a regular on-disk file, applying the same
* realpath + containment guarantees as {@link LocalProtocolHandler.resolve}
* but WITHOUT reading or UTF-8-decoding its contents. Returns null when there
* is no active session or when the URL targets the root listing or a directory;
* throws the handler's not-found and "escapes local root" errors for missing
* files and symlink escapes.
*
* Options are resolved via {@link LocalProtocolHandler.resolveOptions} so the
* caller-options → override → registry order matches router resolution exactly.
* The read tool uses this to detect and emit image files from their real path
* before the text-only resource contract would decode the binary into mojibake.
*/
export async function resolveLocalUrlToFile(View on GitHub (pinned to 9690622007)
Solutions
- Remove the non-regular file from the local root and use a regular file instead.
- Redirect the tool that created the FIFO/socket to a different directory (e.g. /tmp) outside the session artifacts.
- Read device/special files with the standard filesystem tools if genuinely needed, not local://.
Defensive patterns
Strategy: validation
Validate before calling
const st = await fs.stat(resolvedPath);
if (!st.isFile() && !st.isDirectory()) throw new Error(`local:// target must be a regular file or directory, got mode ${st.mode}`); Try / catch
try { resource = await handler.resolve(url, ctx); } catch (e) { if (String(e.message).includes('must resolve to a file or directory')) { /* inspect the target's file type with stat */ } else throw e; } Prevention
- Keep FIFOs, sockets, and device nodes out of the session local artifacts directory.
- Point pipe/socket-creating tools at a scratch dir outside the artifacts root.
- Stat the target yourself before resolving if the directory contents are untrusted.
When it happens
Trigger: Creating a FIFO or unix socket inside the session local root (e.g. mkfifo local-root/pipe) and then resolving local://pipe; a symlink whose realpath target is a device node like /dev/null placed within the root.
Common situations: Scripts or build tools that create named pipes or sockets in the artifacts directory; tests that stub the local root with special files.
Related errors
- unknown filetype: {ft_debug}
- Managed skill "${name}" SKILL.md is not a regular file; refu
- Shared-folder destination escapes its configured root
- Path is not a file: ${pathArg}
- skill:// URL must resolve to a file or directory: ${url.href
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/c636d3985115ad29.
Report an issue: GitHub.