garrytan/gstack · error · Error
Unsupported file URL host: ${parsed.host}. Use file:///<abso
Error message
Unsupported file URL host: ${parsed.host}. Use file:///<absolute-path> for local files. What it means
The navigation validator rejects file: URLs whose host component is anything other than empty or 'localhost'. The check at url-validation.ts:240-247 (parsed.host !== '' && parsed.host.toLowerCase() !== 'localhost') blocks UNC and network-share paths, which could otherwise let a sandboxed browser read from remote shares. Only local absolute paths via file:///<path> are permitted.
Source
Thrown at browse/src/url-validation.ts:246
export async function validateNavigationUrl(url: string): Promise<string> {
// Normalize non-standard file:// shapes before the URL parser sees them.
let normalized = url;
if (url.toLowerCase().startsWith('file:')) {
normalized = normalizeFileUrl(url);
}
let parsed: URL;
try {
parsed = new URL(normalized);
} catch {
throw new Error(`Invalid URL: ${url}`);
}
// file:// path: validate against safe-dirs and allow; otherwise defer to http(s) logic.
if (parsed.protocol === 'file:') {
// Reject non-empty non-localhost hosts (UNC / network paths).
if (parsed.host !== '' && parsed.host.toLowerCase() !== 'localhost') {
throw new Error(
`Unsupported file URL host: ${parsed.host}. Use file:///<absolute-path> for local files.`
);
}
// Convert URL → filesystem path with proper decoding (handles %20, %2F, etc.)
// fileURLToPath strips query + hash; we reattach them after validation so SPA
// fixture URLs like file:///tmp/app.html?route=home#login survive intact.
let fsPath: string;
try {
fsPath = fileURLToPath(parsed);
} catch (e: any) {
throw new Error(`Invalid file URL: ${url} (${e.message})`);
}
// Reject path traversal after decoding — e.g. file:///tmp/safe%2F..%2Fetc/passwd
// Note: fileURLToPath doesn't collapse .., so a literal '..' in the decoded path
// is suspicious. path.resolve will normalize it; check the result against safe dirs.
validateReadPath(fsPath);View on GitHub (pinned to 94993f7401)
Solutions
- Use three slashes plus an absolute path: file:///home/user/foo.html or file:///C:/Users/me/foo.html.
- For UNC/network shares, mount the share locally first (e.g. /mnt/share) and reference the mount point with file:///
- If a literal host is intentional for a local server, use http://localhost:<port> instead of file://localhost
- Build file URLs programmatically with pathToFileURL(absolutePath).href so the host is always empty
Example fix
// before
await goto('file://server/share/report.html')
// after
await goto('file:///mnt/share/report.html')
// or build canonically
import { pathToFileURL } from 'node:url'
await goto(pathToFileURL('/mnt/share/report.html').href) Defensive patterns
Strategy: validation
Validate before calling
import { pathToFileURL } from 'node:url'
// Build file URLs canonically so host is always empty.
function toFileUrl(absPath: string): string {
return pathToFileURL(absPath).href
}
// Reject any file: URL with a non-empty host before calling goto.
function isLocalFileUrl(u: string): boolean {
let p: URL
try { p = new URL(u) } catch { return false }
if (p.protocol !== 'file:') return false
return p.host === '' || p.host.toLowerCase() === 'localhost'
} Type guard
function isSafeFileUrl(u: string): u is string {
try {
const p = new URL(u)
return p.protocol === 'file:' && (p.host === '' || p.host.toLowerCase() === 'localhost')
} catch { return false }
} Prevention
- Always build file URLs with pathToFileURL(abs).href instead of string concatenation
- Prefer file:/// (three slashes) plus an absolute path
- Mount network shares locally before referencing them
When it happens
Trigger: Calling browse goto (or any path through validateNavigationUrl) with a file: URL that carries a host, e.g. file://server/share/x.html, file://nas/data.html, or file://home/user/foo.html (two slashes + name = host 'home'). On Windows, a pasted UNC path file://\\server\share also parses to a non-empty host.
Common situations: Pasting a Windows UNC path verbatim; writing file:// instead of file:/// before an absolute path; templating URLs with a variable that injects a hostname; macOS/Linux users copying a 'file://host' link from a file manager.
Related errors
- Invalid file URL: ${url} (${e.message})
- Blocked: scheme "${parsed.protocol}" is not allowed. Only ht
- Blocked: ${parsed.hostname} is a cloud metadata endpoint. Ac
- Blocked: ${parsed.hostname} resolves to a cloud metadata IP.
- Invalid file URL: file:/// has no path. Use file:///<absolut
AI-assisted analysis of garrytan/gstack@94993f7401 (2026-08-12).
Data as JSON: /api/errors/553f11740b45663f.
Report an issue: GitHub.