can1357/oh-my-pi · error
Vault file not found: ${parsed.url}
Error message
Vault file not found: ${parsed.url} What it means
#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.
Source
Thrown at packages/coding-agent/src/internal-urls/vault-protocol.ts:876
async #readFile(
parsed: Extract<ParsedVaultUrl, { kind: "fs-file" }>,
context?: ResolveContext,
): Promise<InternalResource> {
const { root, targetPath } = await this.#resolveFsTarget(parsed, context);
const parentDir = path.dirname(targetPath);
try {
const realParent = await fs.promises.realpath(parentDir);
ensureWithinRoot(realParent, root);
} catch (error) {
if (!isEnoent(error)) throw error;
}
let realTargetPath: string;
try {
realTargetPath = await fs.promises.realpath(targetPath);
} catch (error) {
if (isEnoent(error)) {
throw new Error(`Vault file not found: ${parsed.url}`);
}
throw error;
}
ensureWithinRoot(realTargetPath, root);
const stat = await fs.promises.stat(realTargetPath);
if (stat.isDirectory()) {
return this.#listDir(parsed, context);
}
if (!stat.isFile()) {
throw new Error(`vault:// URL must resolve to a file or directory: ${parsed.url}`);
}
const content = await Bun.file(realTargetPath).text();
return {
url: parsed.url,
content,
contentType: getContentType(realTargetPath),
size: Buffer.byteLength(content, "utf-8"),View on GitHub (pinned to 9690622007)
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.
Example fix
// before
const res = await resolveInternalUrl("vault://_/old-name.md"); // renamed note
// after
const res = await resolveInternalUrl("vault://_/new-name.md"); Defensive patterns
Strategy: try-catch
Validate before calling
import * as fs from "node:fs/promises";
try {
await fs.access(targetPath);
} catch {
throw new Error(`Vault target does not exist yet: ${targetPath} (create it or fix the URL)`);
} Try / catch
try {
return await resolveInternalUrl(url);
} catch (err) {
if (err instanceof Error && err.message.startsWith("Vault file not found:")) {
// treat as not-found resource or offer to create the file
return null;
}
throw err;
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
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
- File not found: ${path}
- Local file not found: ${url.href}
- File not found: ${resolvedPath}
- File not found: ${targetPath}
- vault:// URL must resolve to a directory: ${parsed.url}
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/af8dbaf35f5e8455.
Report an issue: GitHub.