garrytan/gstack · error · Error

Invalid file URL: file://~/ would list the home directory. U

Error message

Invalid file URL: file://~/ would list the home 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 and no filename. Like the `.` case, this would trigger a directory listing of the home directory, so the library demands an explicit filename.

Source

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

    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;
  }

  // localhost host explicitly allowed: file://localhost/<abs> (pass through to standard parser).
  if (afterDoubleSlash.toLowerCase().startsWith('localhost/')) {

View on GitHub (pinned to 94993f7401)

Solutions

  1. Always include the filename: `file://~/Documents/report.html`.
  2. When constructing from `~` + filename, default the filename if empty.
  3. Prefer pathToFileURL(path.join(os.homedir(), filename)).href for programmatic construction.

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 requireHomeFilename(u: string): void {
  const lower = u.toLowerCase();
  if (lower === 'file://~' || lower === 'file://~/') {
    throw new Error('file://~/ requires a filename');
  }
}

Type guard

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

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://~/${rest}` where rest is empty.

Common situations: A user trying to 'browse my home folder'; a fixture loader using `~` as a base and joining an empty filename; a docs example that trimmed the final path segment.

Related errors


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