garrytan/gstack · error · Error
Invalid URL: ${url}
Error message
Invalid URL: ${url} What it means
Thrown by validateNavigationUrl when the (already-normalized) URL cannot be parsed by the standard URL constructor. This is the catch-all for malformed inputs that survived normalizeFileUrl but are still not a valid http/https/file URL — empty strings, embedded whitespace, missing scheme, broken percent-encoding, or unsupported schemes that the parser rejects outright.
Source
Thrown at browse/src/url-validation.ts:239
*
* Callers (keep this list current, grep before removing):
* - write-commands.ts:goto
* - meta-commands.ts:diff (both URL args)
* - browser-manager.ts:newTab
* - browser-manager.ts:restoreState
*/
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) {View on GitHub (pinned to 94993f7401)
Solutions
- Ensure the URL has an explicit http://, https://, or file:// scheme.
- Trim whitespace and reject empty strings before calling validateNavigationUrl: `if (!url.trim()) throw ...`.
- Prepend `https://` when the user types a bare hostname: `url = /^https?:\/\//.test(s) ? s : 'https://' + s`.
- Validate with `new URL(url)` in a try/catch at the caller boundary to give a friendlier error.
Example fix
// before
await goto(userInput); // userInput === 'example.com' → throws
// after
const safe = /^https?:\/\//i.test(userInput) ? userInput : `https://${userInput}`;
await goto(safe); Defensive patterns
Strategy: validation
Validate before calling
function normalizeUserUrl(input: string): string {
const trimmed = input.trim();
if (!trimmed) throw new Error('Invalid URL: empty input');
if (/^file:/i.test(trimmed)) return trimmed; // file: handled by normalizeFileUrl
const withScheme = /^[a-z][a-z0-9+.-]*:\/\//i.test(trimmed) ? trimmed : `https://${trimmed}`;
try { new URL(withScheme); } catch { throw new Error(`Invalid URL: ${input}`); }
return withScheme;
} Type guard
function isValidUrl(u: string): boolean {
try { new URL(u); return true; } catch { return false; }
} Try / catch
try {
await goto(url);
} catch (e: any) {
if (/^Invalid URL:/.test(e.message)) {
const fixed = /^https?:\/\//i.test(url) ? url : `https://${url}`;
await goto(fixed);
} else throw e;
} Prevention
- Trim and reject empty strings before calling validateNavigationUrl.
- Prepend https:// when the user input is a bare hostname.
- Run `new URL(input)` at your own boundary to give a friendlier error.
- Strip smart quotes and stray whitespace from pasted URLs.
When it happens
Trigger: Calling goto with `''`, `'ht tp://x'` (embedded space), `'example.com'` (no scheme — the parser requires one), `'javascript:void(0)'` after the scheme blocklist, or any string the WHATWG URL constructor rejects.
Common situations: User input without a scheme; copy-paste introducing a leading/trailing space or smart quote; templating that left the URL empty when a variable was undefined; non-ASCII hostnames without IDNA normalization.
Related errors
- 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
- Unsupported file URL host: ${parsed.host}. Use file:///<abso
- Invalid file URL: ${url} (${e.message})
AI-assisted analysis of garrytan/gstack@94993f7401 (2026-08-12).
Data as JSON: /api/errors/f5a478a017209791.
Report an issue: GitHub.