{"record":{"id":"c636d3985115ad29","repo":"can1357/oh-my-pi","slug":"local-url-must-resolve-to-a-file-or-directory","errorCode":null,"errorMessage":"local:// URL must resolve to a file or directory: ${url.href}","messagePattern":"local:// URL must resolve to a file or directory: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/internal-urls/local-protocol.ts","lineNumber":391,"sourceCode":"\n\tlet realTargetPath: string;\n\ttry {\n\t\trealTargetPath = await fs.realpath(targetPath);\n\t} catch (error) {\n\t\tif (isEnoent(error)) {\n\t\t\tthrow new Error(`Local file not found: ${url.href}`);\n\t\t}\n\t\tthrow error;\n\t}\n\n\tensureWithinRoot(realTargetPath, resolvedRoot);\n\n\tconst stat = await fs.stat(realTargetPath);\n\tif (stat.isDirectory()) {\n\t\treturn { kind: \"directory\", path: realTargetPath };\n\t}\n\tif (!stat.isFile()) {\n\t\tthrow new Error(`local:// URL must resolve to a file or directory: ${url.href}`);\n\t}\n\treturn { kind: \"file\", path: realTargetPath, size: stat.size };\n}\n\n/**\n * Resolve a local:// URL to a regular on-disk file, applying the same\n * realpath + containment guarantees as {@link LocalProtocolHandler.resolve}\n * but WITHOUT reading or UTF-8-decoding its contents. Returns null when there\n * is no active session or when the URL targets the root listing or a directory;\n * throws the handler's not-found and \"escapes local root\" errors for missing\n * files and symlink escapes.\n *\n * Options are resolved via {@link LocalProtocolHandler.resolveOptions} so the\n * caller-options → override → registry order matches router resolution exactly.\n * The read tool uses this to detect and emit image files from their real path\n * before the text-only resource contract would decode the binary into mojibake.\n */\nexport async function resolveLocalUrlToFile(","sourceCodeStart":373,"sourceCodeEnd":409,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/internal-urls/local-protocol.ts#L373-L409","documentation":"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.","triggerScenarios":"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.","commonSituations":"Scripts or build tools that create named pipes or sockets in the artifacts directory; tests that stub the local root with special files.","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://."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"const st = await fs.stat(resolvedPath);\nif (!st.isFile() && !st.isDirectory()) throw new Error(`local:// target must be a regular file or directory, got mode ${st.mode}`);","typeGuard":null,"tryCatchPattern":"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; }","preventionTips":["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."],"tags":["filesystem","file-type","validation"],"backgroundTag":"unsupported-file-type","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}