{"record":{"id":"453d8e1d021ff6e4","repo":"garrytan/gstack","slug":"invalid-file-url-url-use-file-absolute-pa","errorCode":null,"errorMessage":"Invalid file URL: ${url}. Use file:///<absolute-path> or file://./<rel> or file://~/<rel>.","messagePattern":"Invalid file URL: (.+?)\\. Use file:///<absolute-path> or file://\\./<rel> or file://~/<rel>\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"browse/src/url-validation.ts","lineNumber":161,"sourceCode":"  else if (hIdx >= 0) delimIdx = hIdx;\n\n  const pathPart = delimIdx >= 0 ? url.slice(0, delimIdx) : url;\n  const trailing = delimIdx >= 0 ? url.slice(delimIdx) : '';\n\n  const rest = pathPart.slice('file:'.length);\n\n  // file:/// or longer → standard absolute; pass through unchanged (caller validates path).\n  if (rest.startsWith('///')) {\n    // Reject bare root-only (file:/// with nothing after)\n    if (rest === '///' || rest === '////') {\n      throw new Error('Invalid file URL: file:/// has no path. Use file:///<absolute-path>.');\n    }\n    return pathPart + trailing;\n  }\n\n  // Everything else: must start with // (we accept file://... only)\n  if (!rest.startsWith('//')) {\n    throw new Error(`Invalid file URL: ${url}. Use file:///<absolute-path> or file://./<rel> or file://~/<rel>.`);\n  }\n\n  const afterDoubleSlash = rest.slice(2);\n\n  // Reject empty (file://) and trailing-slash-only (file://./ listing cwd).\n  if (afterDoubleSlash === '') {\n    throw new Error('Invalid file URL: file:// is empty. Use file:///<absolute-path>.');\n  }\n  if (afterDoubleSlash === '.' || afterDoubleSlash === './') {\n    throw new Error('Invalid file URL: file://./ would list the current directory. Use file://./<filename> to render a specific file.');\n  }\n  if (afterDoubleSlash === '~' || afterDoubleSlash === '~/') {\n    throw new Error('Invalid file URL: file://~/ would list the home directory. Use file://~/<filename> to render a specific file.');\n  }\n\n  // Home-relative: file://~/<rel>\n  if (afterDoubleSlash.startsWith('~/')) {\n    const rel = afterDoubleSlash.slice(2);","sourceCodeStart":143,"sourceCodeEnd":179,"githubUrl":"https://github.com/garrytan/gstack/blob/94993f74012782fd94416dd44b8314f6363a13a4/browse/src/url-validation.ts#L143-L179","documentation":"Thrown by normalizeFileUrl when the input starts with `file:` but the remainder does not begin with `//` (so it is not a valid RFC 8089 authority-and-path form). Catches malformed shapes like `file:path`, `file:/path` (single slash), and `file:./x` that the standard URL parser would misinterpret.","triggerScenarios":"Calling goto with `file:/etc/hosts` (one slash), `file:index.html`, or hand-built strings that omitted the second slash.","commonSituations":"Concatenating `file:` + absolute filesystem path without the `//` authority prefix; copy-pasting a Windows path `file:C:\\x`; templating that uses `file:${process.platform === 'win32' ? '/' : '//'}` and gets the branch wrong.","solutions":["Always use the form `file:///<absolute-path>` (three slashes for Unix absolute paths).","Build file URLs from paths with `require('url').pathToFileURL(absPath).href` — never by string concatenation.","For relative paths use the supported shorthands `file://./<rel>` or `file://~/<rel>`."],"exampleFix":"// before\nawait goto(`file:${absPath}`); // missing //\n// after\nconst { pathToFileURL } = require('url');\nawait goto(pathToFileURL(absPath).href);","handlingStrategy":"validation","validationCode":"const { pathToFileURL } = require('url');\nconst path = require('path');\nfunction fileUrlFromPath(p: string): string {\n  return pathToFileURL(path.resolve(p)).href;\n}","typeGuard":"const isRfc8089AuthorityForm = (u: string): boolean =>\n  /^file:\\/\\/(\\/|\\.|~|localhost\\/).*/.test(u.toLowerCase());","tryCatchPattern":"try {\n  await goto(url);\n} catch (e: any) {\n  if (/^Invalid file URL:.*Use file:\\/\\/\\//.test(e.message)) {\n    await goto(pathToFileURL(path.resolve(url.replace(/^file:/, ''))).href);\n  } else throw e;\n}","preventionTips":["Never build file URLs by string concatenation — always pathToFileURL.","Require three slashes for absolute paths, two slashes + host/path for the supported shorthands.","Reject Windows backslash paths at the boundary and convert with pathToFileURL.","Add a fixture-URL builder helper so the slash count is centralized."],"tags":["url-validation","file-url","rfc-8089","navigation"],"backgroundTag":null,"analyzedSha":"94993f74012782fd94416dd44b8314f6363a13a4","analyzedAt":"2026-08-12T04:06:23.140Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}