{"record":{"id":"d4bcf62865d1f461","repo":"garrytan/gstack","slug":"invalid-file-url-file-has-no-path-use-file","errorCode":null,"errorMessage":"Invalid file URL: file:/// has no path. Use file:///<absolute-path>.","messagePattern":"Invalid file URL: file:/// has no path\\. Use file:///<absolute-path>\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"browse/src/url-validation.ts","lineNumber":154,"sourceCode":"  // Find the FIRST `?` or `#`, whichever comes first, and take everything\n  // after (including the delimiter) as the trailing segment.\n  const qIdx = url.indexOf('?');\n  const hIdx = url.indexOf('#');\n  let delimIdx = -1;\n  if (qIdx >= 0 && hIdx >= 0) delimIdx = Math.min(qIdx, hIdx);\n  else if (qIdx >= 0) delimIdx = qIdx;\n  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  }","sourceCodeStart":136,"sourceCodeEnd":172,"githubUrl":"https://github.com/garrytan/gstack/blob/94993f74012782fd94416dd44b8314f6363a13a4/browse/src/url-validation.ts#L136-L172","documentation":"Thrown by normalizeFileUrl when the input is exactly `file:///` or `file:////` — the file scheme with three slashes and no path. These would normally fall through to Chromium's directory-listing behaviour for the filesystem root, which the library deliberately refuses so the caller must specify a concrete file.","triggerScenarios":"Calling goto/validateNavigationUrl with `file:///`, `file:////`, or programmatically concatenating `file://` + a path that turns out empty.","commonSituations":"A test fixture that builds `file://${process.cwd()}/` and forgets the filename; templating that strips empty path segments; a CLI user typing `browse goto file:///` expecting a 'home' view.","solutions":["Append a concrete absolute path: `file:///tmp/index.html`.","When templating, default to a known index file if the path is empty: `${base}index.html`.","Use pathToFileURL(absPath).href to construct file URLs from a path string so the slash count is always correct."],"exampleFix":"// before\nconst url = `file://${dir}/`; // → 'file:///tmp/x/' or worse\n// after\nconst url = require('url').pathToFileURL(require('path').join(dir, 'index.html')).href;","handlingStrategy":"validation","validationCode":"const { pathToFileURL } = require('url');\nconst path = require('path');\nfunction safeFileUrl(p: string): string {\n  if (!p) throw new Error('Invalid file URL: file:/// has no path.');\n  return pathToFileURL(path.resolve(p)).href;\n}","typeGuard":"const hasNonEmptyPath = (u: string): boolean =>\n  /^file:\\/\\/\\/.+/.test(u) && u !== 'file:////';","tryCatchPattern":"try {\n  await goto(url);\n} catch (e: any) {\n  if (/file:\\/\\/ has no path/.test(e.message)) {\n    await goto(pathToFileURL(path.resolve(defaultIndex)).href);\n  } else throw e;\n}","preventionTips":["Build file URLs with pathToFileURL(absPath).href — never string concat.","Default empty paths to a known index file before building the URL.","Validate the basename is non-empty at the caller boundary.","Avoid trailing slashes in fixture paths."],"tags":["url-validation","file-url","directory-listing","navigation"],"backgroundTag":null,"analyzedSha":"94993f74012782fd94416dd44b8314f6363a13a4","analyzedAt":"2026-08-12T04:06:23.140Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}