{"record":{"id":"dd15c68d0f1cb51b","repo":"can1357/oh-my-pi","slug":"vault-url-must-resolve-to-a-file-or-directory","errorCode":null,"errorMessage":"vault:// URL must resolve to a file or directory: ${parsed.url}","messagePattern":"vault:// URL must resolve to a file or directory: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/internal-urls/vault-protocol.ts","lineNumber":886,"sourceCode":"\t\t\tif (!isEnoent(error)) throw error;\n\t\t}\n\n\t\tlet realTargetPath: string;\n\t\ttry {\n\t\t\trealTargetPath = await fs.promises.realpath(targetPath);\n\t\t} catch (error) {\n\t\t\tif (isEnoent(error)) {\n\t\t\t\tthrow new Error(`Vault file not found: ${parsed.url}`);\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\t\tensureWithinRoot(realTargetPath, root);\n\t\tconst stat = await fs.promises.stat(realTargetPath);\n\t\tif (stat.isDirectory()) {\n\t\t\treturn this.#listDir(parsed, context);\n\t\t}\n\t\tif (!stat.isFile()) {\n\t\t\tthrow new Error(`vault:// URL must resolve to a file or directory: ${parsed.url}`);\n\t\t}\n\n\t\tconst content = await Bun.file(realTargetPath).text();\n\t\treturn {\n\t\t\turl: parsed.url,\n\t\t\tcontent,\n\t\t\tcontentType: getContentType(realTargetPath),\n\t\t\tsize: Buffer.byteLength(content, \"utf-8\"),\n\t\t\tsourcePath: realTargetPath,\n\t\t};\n\t}\n\n\tasync #writeFile(\n\t\tparsed: Extract<ParsedVaultUrl, { kind: \"fs-file\" }>,\n\t\tcontent: string,\n\t\tcontext?: WriteContext,\n\t): Promise<void> {\n\t\tconst { root, targetPath } = await this.#resolveFsTarget(parsed, context);","sourceCodeStart":868,"sourceCodeEnd":904,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/internal-urls/vault-protocol.ts#L868-L904","documentation":"In the file-read path, after realpath and the directory branch, the handler requires stat.isFile(). Anything that is neither a directory nor a regular file — FIFOs, sockets, devices, etc. — triggers this error with the original URL. It is a defensive check so the handler never reads non-regular filesystem objects through vault://.","triggerScenarios":"Resolving a vault:// URL whose target realpath is a special file (socket, FIFO, device node) rather than a regular file or directory, e.g. a symlink inside the vault pointing at a Unix socket.","commonSituations":"Vaults containing unexpected symlink targets into /tmp or /dev; stray special files created by other tooling inside the vault directory.","solutions":["Point the URL at a regular file or directory instead of the special file.","Remove or relocate the symlink/special file from the vault if it was created accidentally.","If you must access such a target, read it through a filesystem API outside vault://."],"exampleFix":"// before\nawait resolveInternalUrl(\"vault://_/ipc.sock\"); // special file -> throws\n// after\nawait resolveInternalUrl(\"vault://_/ipc-config.md\"); // regular file","handlingStrategy":"type-guard","validationCode":"import * as fs from \"node:fs/promises\";\nconst stat = await fs.stat(targetPath);\nif (!stat.isFile() && !stat.isDirectory()) {\n  throw new Error(`${targetPath} is a special file; vault:// only reads regular files/directories`);\n}","typeGuard":"function isRegularOrDir(stat: fs.Stats): boolean {\n  return stat.isFile() || stat.isDirectory();\n}","tryCatchPattern":"try {\n  return await resolveInternalUrl(url);\n} catch (err) {\n  if (err instanceof Error && err.message.includes(\"must resolve to a file or directory\")) {\n    // skip this target; it is a socket/FIFO/device inside the vault\n    return null;\n  }\n  throw err;\n}","preventionTips":["Audit vault directories for stray sockets/FIFOs and symlinks to special files.","Skip non-regular entries when enumerating vault contents for resolution.","Use lstat to detect suspicious symlinks before reading."],"tags":["vault","filesystem","kind-mismatch"],"backgroundTag":"not-a-regular-file","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}