garrytan/gstack · error · Error
Invalid file URL: ${url} (${e.message})
Error message
Invalid file URL: ${url} (${e.message}) What it means
Wraps a failure from Node's fileURLToPath(parsed) at url-validation.ts:255-258. The URL already parsed successfully and passed the host check, but Node's own file-URL-to-path conversion rejected it. The appended e.message is the underlying Node error, which is the key to the real cause.
Source
Thrown at browse/src/url-validation.ts:258
}
// 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);
// Return the canonical file:// URL derived from the filesystem path + original
// query + hash. This guarantees page.goto() gets a well-formed URL regardless
// of input shape while preserving SPA route/query params.
return pathToFileURL(fsPath).href + parsed.search + parsed.hash;
}
if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') {
throw new Error(
`Blocked: scheme "${parsed.protocol}" is not allowed. Only http:, https:, and file: URLs are permitted.`
);
}View on GitHub (pinned to 94993f7401)
Solutions
- Read the appended Node error message first — it names the exact malformation
- Construct the URL with pathToFileURL(absolutePath).href rather than string-building it
- Pass a plain absolute filesystem path and let the library form the file URL
- On Windows, ensure drive letters are present (file:///C:/...) and avoid forward-slash roots
Example fix
// before (hand-built, fails fileURLToPath on Windows)
await goto('file://' + rawPath)
// after
import { pathToFileURL } from 'node:url'
await goto(pathToFileURL(rawPath).href) Defensive patterns
Strategy: validation
Validate before calling
import { fileURLToPath, pathToFileURL } from 'node:url'
// Pre-validate that Node can convert the file URL to a path.
function isValidFileUrl(u: string): boolean {
try { fileURLToPath(new URL(u)); return true } catch { return false }
} Type guard
function isConvertibleFileUrl(u: string): u is string {
try { fileURLToPath(new URL(u)); return true } catch { return false }
} Try / catch
try {
await goto(normalizedUrl)
} catch (e) {
if (e.message.startsWith('Invalid file URL:')) {
// rebuild canonically and retry once
await goto(pathToFileURL(absPath).href)
} else throw e
} Prevention
- Construct file URLs via pathToFileURL so Node can always convert them back
- On Windows, ensure drive-letter roots (file:///C:/...)
- Avoid hand-rolling percent-encoding in paths
When it happens
Trigger: A file: URL that new URL() accepts but fileURLToPath rejects: malformed percent-encoding, an incompatible Windows drive-letter form, or a path that cannot be represented on the current OS. Most common on Windows with non-drive roots like file:///usr/local or bad %2F sequences.
Common situations: Cross-platform path strings rendered into file URLs naively; string concatenation that produces double-encoded sequences; Windows builds receiving Unix-style roots.
Related errors
- Unsupported file URL host: ${parsed.host}. Use file:///<abso
- Invalid file URL: file:/// has no path. Use file:///<absolut
- Invalid file URL: ${url}. Use file:///<absolute-path> or fil
- Invalid file URL: file:// is empty. Use file:///<absolute-pa
- Invalid URL: ${url}
AI-assisted analysis of garrytan/gstack@94993f7401 (2026-08-12).
Data as JSON: /api/errors/7da5ef9328b0b769.
Report an issue: GitHub.