{"record":{"id":"af8dbaf35f5e8455","repo":"can1357/oh-my-pi","slug":"vault-file-not-found-parsed-url","errorCode":null,"errorMessage":"Vault file not found: ${parsed.url}","messagePattern":"Vault file not found: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/internal-urls/vault-protocol.ts","lineNumber":876,"sourceCode":"\tasync #readFile(\n\t\tparsed: Extract<ParsedVaultUrl, { kind: \"fs-file\" }>,\n\t\tcontext?: ResolveContext,\n\t): Promise<InternalResource> {\n\t\tconst { root, targetPath } = await this.#resolveFsTarget(parsed, context);\n\t\tconst parentDir = path.dirname(targetPath);\n\t\ttry {\n\t\t\tconst realParent = await fs.promises.realpath(parentDir);\n\t\t\tensureWithinRoot(realParent, root);\n\t\t} catch (error) {\n\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\"),","sourceCodeStart":858,"sourceCodeEnd":894,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/internal-urls/vault-protocol.ts#L858-L894","documentation":"#resolveFsTarget's realpath step wraps fs.promises.realpath(targetPath) and converts ENOENT failures into this friendly error carrying the original vault:// URL. Non-ENOENT errors are rethrown untouched. It means the referenced vault path (including any symlink chain) does not exist on disk.","triggerScenarios":"Resolving a vault:// URL whose target file does not exist — realpath fails with ENOENT — e.g. vault://_/notes/missing.md where no such note was ever created.","commonSituations":"Deleted or renamed notes referenced by stale links/URLs; typos in the path; vault moved to a different location so registered paths no longer resolve.","solutions":["Create the missing file (the write path supports creating new files) or correct the URL path.","Check the exact spelling and directory of the note in the vault.","Re-register/refresh the vault path if the vault was moved (`obsidian vault info path` / vault list).","Catch this error and treat the URL as a not-found resource if your code expects missing notes."],"exampleFix":"// before\nconst res = await resolveInternalUrl(\"vault://_/old-name.md\"); // renamed note\n// after\nconst res = await resolveInternalUrl(\"vault://_/new-name.md\");","handlingStrategy":"try-catch","validationCode":"import * as fs from \"node:fs/promises\";\ntry {\n  await fs.access(targetPath);\n} catch {\n  throw new Error(`Vault target does not exist yet: ${targetPath} (create it or fix the URL)`);\n}","typeGuard":null,"tryCatchPattern":"try {\n  return await resolveInternalUrl(url);\n} catch (err) {\n  if (err instanceof Error && err.message.startsWith(\"Vault file not found:\")) {\n    // treat as not-found resource or offer to create the file\n    return null;\n  }\n  throw err;\n}","preventionTips":["Check note existence (or keep an index) before resolving vault links.","Clean up stale references when notes are renamed or deleted.","Re-verify vault registration after the vault directory moves."],"tags":["vault","filesystem","file-not-found"],"backgroundTag":"file-not-found","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}