{"record":{"id":"124674e96619483e","repo":"garrytan/gstack","slug":"unsupported-file-url-host-segment-use-file","errorCode":null,"errorMessage":"Unsupported file URL host: ${segment}. Use file:///<absolute-path> for local files (network/UNC paths are not supported).","messagePattern":"Unsupported file URL host: (.+?)\\. Use file:///<absolute-path> for local files \\(network/UNC paths are not supported\\)\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"browse/src/url-validation.ts","lineNumber":206,"sourceCode":"    return pathToFileURL(absPath).href + trailing;\n  }\n\n  // localhost host explicitly allowed: file://localhost/<abs> (pass through to standard parser).\n  if (afterDoubleSlash.toLowerCase().startsWith('localhost/')) {\n    return pathPart + trailing;\n  }\n\n  // Ambiguous: file://<segment>/<rest> — treat as cwd-relative ONLY if <segment> is a\n  // simple path name (no dots, no colons, no backslashes, no percent-encoding, no\n  // IPv6 brackets, no Windows drive letter pattern).\n  const firstSlash = afterDoubleSlash.indexOf('/');\n  const segment = firstSlash === -1 ? afterDoubleSlash : afterDoubleSlash.slice(0, firstSlash);\n\n  // Reject host-like segments: dotted names (docs.v1), IPs (127.0.0.1), IPv6 ([::1]),\n  // drive letters (C:), percent-encoded, or backslash paths.\n  const looksLikeHost = /[.:\\\\%]/.test(segment) || segment.startsWith('[');\n  if (looksLikeHost) {\n    throw new Error(\n      `Unsupported file URL host: ${segment}. Use file:///<absolute-path> for local files (network/UNC paths are not supported).`\n    );\n  }\n\n  // Simple-segment cwd-relative: file://docs/page.html → cwd/docs/page.html\n  const absPath = path.resolve(process.cwd(), afterDoubleSlash);\n  return pathToFileURL(absPath).href + trailing;\n}\n\n/**\n * Validate a navigation URL and return a normalized version suitable for page.goto().\n *\n * Callers MUST use the return value — normalization of non-standard file:// forms\n * only takes effect at the navigation site, not at the original URL.\n *\n * Callers (keep this list current, grep before removing):\n *   - write-commands.ts:goto\n *   - meta-commands.ts:diff (both URL args)","sourceCodeStart":188,"sourceCodeEnd":224,"githubUrl":"https://github.com/garrytan/gstack/blob/94993f74012782fd94416dd44b8314f6363a13a4/browse/src/url-validation.ts#L188-L224","documentation":"Thrown by normalizeFileUrl when the segment after `file://` looks like a host rather than a simple path name — anything containing `.`, `:`, `\\`, `%`, or starting with `[`. The library does not support network/UNC/file-share URLs and refuses them rather than silently treating them as cwd-relative.","triggerScenarios":"Calling goto with `file://docs.v1/page.html` (dotted host), `file://127.0.0.1/x`, `file://[::1]/x` (IPv6), `file://C:/x` (Windows drive letter with colon), `file://host%20name/x` (percent-encoded host), or `file://share\\dir/x` (backslash).","commonSituations":"Windows users pasting `file://C:/Users/x` (should be `file:///C:/Users/x` or `file:///C:\\Users\\x`); SMB/UNC paths `file://server/share`; copy-pasting a URL with a hostname where the local-path form was expected.","solutions":["Use the three-slash local form: `file:///C:/Users/x.html` on Windows, `file:///tmp/x.html` on Unix.","For network/UNC shares, copy the file locally first — network file URLs are not supported.","If the segment was meant as a relative path that happens to contain a dot, use the explicit `file://./` prefix: `file://./docs.v1/page.html` is still rejected; rename or use `file:///cwd/docs.v1/page.html` via pathToFileURL."],"exampleFix":"// before\nawait goto('file://C:/Users/me/index.html'); // 'C:' looks like host\n// after\nawait goto('file:///C:/Users/me/index.html'); // three slashes = local absolute","handlingStrategy":"validation","validationCode":"const path = require('path');\nconst { pathToFileURL } = require('url');\nfunction localFileUrl(p: string): string {\n  // Always three-slash form for local absolute paths; rejects UNC/network.\n  const abs = path.resolve(p);\n  return pathToFileURL(abs).href;\n}","typeGuard":"const isLocalFileUrl = (u: string): boolean =>\n  /^file:\\/\\/\\/(?!\\/)/.test(u) && // three slashes, not four\n  !/[.:\\\\%]/.test(u.split('/')[3] ?? ''); // first path segment doesn't look like a host","tryCatchPattern":"try {\n  await goto(url);\n} catch (e: any) {\n  if (/Unsupported file URL host/.test(e.message)) {\n    // user passed a Windows drive letter or UNC — coerce to local three-slash form\n    const local = url.replace(/^file:\\/\\//i, 'file:///');\n    await goto(local);\n  } else throw e;\n}","preventionTips":["On Windows use three slashes: `file:///C:/Users/x.html`.","Copy network/UNC files locally before navigating to them.","Build file URLs with pathToFileURL so the slash count is automatic.","Reject `file://host/...` inputs at your URL boundary."],"tags":["url-validation","file-url","unc-paths","windows","host-rejection"],"backgroundTag":null,"analyzedSha":"94993f74012782fd94416dd44b8314f6363a13a4","analyzedAt":"2026-08-12T04:06:23.140Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}