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

  1. Ensure the URL has an explicit http://, https://, or file:// scheme.
  2. Trim whitespace and reject empty strings before calling validateNavigationUrl: `if (!url.trim()) throw ...`.
  3. Prepend `https://` when the user types a bare hostname: `url = /^https?:\/\//.test(s) ? s : 'https://' + s`.
  4. 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

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


AI-assisted analysis of garrytan/gstack@94993f7401 (2026-08-12). Data as JSON: /api/errors/f5a478a017209791. Report an issue: GitHub.