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

  1. Use three slashes plus an absolute path: file:///home/user/foo.html or file:///C:/Users/me/foo.html.
  2. For UNC/network shares, mount the share locally first (e.g. /mnt/share) and reference the mount point with file:///
  3. If a literal host is intentional for a local server, use http://localhost:<port> instead of file://localhost
  4. 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

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


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