garrytan/gstack · error · Error

Invalid file URL: file://./ would list the current directory

Error message

Invalid file URL: file://./ would list the current directory. Use file://./<filename> to render a specific file.

What it means

Thrown by normalizeFileUrl when the input is `file://.` or `file://./` — the file scheme with `.` as the authority. These would make Chromium list the current working directory, which is a different product surface and a potential information leak, so the library demands an explicit filename.

Source

Thrown at browse/src/url-validation.ts:171

    if (rest === '///' || rest === '////') {
      throw new Error('Invalid file URL: file:/// has no path. Use file:///<absolute-path>.');
    }
    return pathPart + trailing;
  }

  // Everything else: must start with // (we accept file://... only)
  if (!rest.startsWith('//')) {
    throw new Error(`Invalid file URL: ${url}. Use file:///<absolute-path> or file://./<rel> or file://~/<rel>.`);
  }

  const afterDoubleSlash = rest.slice(2);

  // Reject empty (file://) and trailing-slash-only (file://./ listing cwd).
  if (afterDoubleSlash === '') {
    throw new Error('Invalid file URL: file:// is empty. Use file:///<absolute-path>.');
  }
  if (afterDoubleSlash === '.' || afterDoubleSlash === './') {
    throw new Error('Invalid file URL: file://./ would list the current directory. Use file://./<filename> to render a specific file.');
  }
  if (afterDoubleSlash === '~' || afterDoubleSlash === '~/') {
    throw new Error('Invalid file URL: file://~/ would list the home directory. Use file://~/<filename> to render a specific file.');
  }

  // Home-relative: file://~/<rel>
  if (afterDoubleSlash.startsWith('~/')) {
    const rel = afterDoubleSlash.slice(2);
    const absPath = path.join(os.homedir(), rel);
    return pathToFileURL(absPath).href + trailing;
  }

  // cwd-relative with explicit ./ : file://./<rel>
  if (afterDoubleSlash.startsWith('./')) {
    const rel = afterDoubleSlash.slice(2);
    const absPath = path.resolve(process.cwd(), rel);
    return pathToFileURL(absPath).href + trailing;
  }

View on GitHub (pinned to 94993f7401)

Solutions

  1. Always include the filename: `file://./index.html`.
  2. When constructing from a directory + filename, default the filename to 'index.html' if empty.
  3. Validate that the joined path has a non-empty basename before building the URL.

Example fix

// before
await goto(`file://./${name}/`); // name empty → file://./
// after
const file = name || 'index.html';
await goto(`file://./${file}`);
Defensive patterns

Strategy: validation

Validate before calling

function requireFileFilename(u: string): void {
  const lower = u.toLowerCase();
  if (lower === 'file://.' || lower === 'file://./') {
    throw new Error('file://./ requires a filename');
  }
}

Type guard

const hasCwdRelativeFilename = (u: string): boolean => {
  const m = /^file:\/\/(\.\/)?(.+)$/i.exec(u);
  return !!m && m[2].length > 0 && m[2] !== '/';
};

Try / catch

try {
  await goto(url);
} catch (e: any) {
  if (/file:\/\.\/ would list/.test(e.message)) {
    await goto(`file://./index.html`);
  } else throw e;
}

Prevention

When it happens

Trigger: Calling goto with `file://./` (trailing slash, no filename); building a URL as `file://${rel}/` where rel is `.`; auto-completing a path with a trailing slash.

Common situations: A user trying to 'open the current folder'; a fixture loader that joins `./` + '' when no fixture name is provided; IDE tab-completion inserting a trailing slash.

Related errors


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