can1357/oh-my-pi · error
Invalid URL encoding in vault:// path: ${url.href}
Error message
Invalid URL encoding in vault:// path: ${url.href} What it means
`decodeVaultPath` decodes the vault:// URL pathname with `decodeURIComponent` after normalizing backslashes to slashes. If the percent-encoding is malformed (e.g. a stray `%` not followed by two hex digits, or a truncated escape), `decodeURIComponent` throws a URIError which is rethrown as this descriptive error including the full URL href. This happens before relative-path validation, so it is purely about URL syntax.
Source
Thrown at packages/coding-agent/src/internal-urls/vault-protocol.ts:159
function decodeVaultPath(url: InternalUrl): {
rawPathname: string;
relativePath: string;
hasPath: boolean;
isDirectory: boolean;
} {
const rawPathname = url.rawPathname ?? url.pathname;
const hasPath = rawPathname !== undefined && rawPathname !== "" && rawPathname !== "/";
const isDirectory = rawPathname === "/" || rawPathname.endsWith("/");
if (!hasPath) {
return { rawPathname, relativePath: "", hasPath: false, isDirectory };
}
let decoded: string;
try {
decoded = decodeURIComponent(rawPathname.slice(1).replaceAll("\\", "/"));
} catch {
throw new Error(`Invalid URL encoding in vault:// path: ${url.href}`);
}
try {
validateRelativePath(decoded);
} catch (error) {
throw toVaultValidationError(error);
}
return { rawPathname, relativePath: decoded.replace(/\/+$/, ""), hasPath: true, isDirectory };
}
function paramsFromUrl(url: InternalUrl): VaultParams {
const params: VaultParams = {};
for (const [key, value] of url.searchParams) {
params[key] = value === "" ? true : value;
}
return params;
}View on GitHub (pinned to 9690622007)
Solutions
- Percent-encode all `%` characters in the path as `%25` (use encodeURIComponent per path segment).
- Regenerate the URL instead of hand-editing it; use the encodeRelativePath-style scheme (encodeURIComponent each segment joined by `/`).
- Rename the file so its name contains no raw `%` characters, then use the encoded form.
Example fix
// before
const url = `vault://_/notes/${name}.md`; // name = "100% done"
// after
const url = `vault://_/notes/${encodeURIComponent(name)}.md`; // "100%25 done" Defensive patterns
Strategy: validation
Validate before calling
function isWellFormedPercentEncoding(s: string): boolean {
// every '%' must be followed by two hex digits
return !/(^|[^%])%([^0-9A-Fa-f]{2}|$)|%$/.test(s) || !/%(?![0-9A-Fa-f]{2})/.test(s);
}
// equivalently: decodeURIComponent(test) inside try/catch before building the URL Type guard
function hasValidEncoding(rawPathname: string): boolean {
try { decodeURIComponent(rawPathname.replace(/\\/g, "/").slice(1)); return true; }
catch { return false; }
} Try / catch
try {
const res = await handler.resolve(parseInternalUrl(url));
} catch (err) {
if (err instanceof Error && err.message.startsWith("Invalid URL encoding in vault://")) {
url = reEncodeUrl(url); // encodeURIComponent each path segment
} else throw err;
} Prevention
- Always encodeURIComponent each path segment; never interpolate raw file names
- Escape literal `%` as `%25` when hand-writing URLs
- Avoid copy-paste truncation of URLs — verify no dangling `%` at the end
- Round-trip test: decodeURIComponent(encode(x)) === x before using a generated URL
When it happens
Trigger: parseVaultUrl -> decodeVaultPath is called with a URL like `vault://_/notes/100%.md`, `vault://_/a%2b%zz.txt`, or a truncated copy-pasted URL ending in `%e`. Any `%` that is not part of a valid `%XX` escape triggers it.
Common situations: Pasting Obsidian URIs or file names containing literal `%` characters unencoded into a vault:// URL; hand-building URLs with string concatenation instead of encodeURIComponent; truncating a URL mid-escape when editing it.
Related errors
- Invalid URL encoding in ssh:// path: ${url.href}
- ssh://: invalid percent-escape in authority "${url.href}"
- Invalid URL encoding in memory:// path: ${url.href}
- ssh:// does not support URL query strings; percent-encode a
- ssh:// does not support URL fragments; percent-encode a lite
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/7346c66321ced44c.
Report an issue: GitHub.