denoland/deno · error · TypeError

ERR_INVALID_URL_SCHEME

ERR_INVALID_URL_SCHEME

Error message

The URL must be of scheme file

What it means

fileURLToPath() only converts file: URLs — after the type check, any other scheme throws ERR_INVALID_URL_SCHEME ('The URL must be of scheme file'). In Deno this is common: modules imported from https:// have an https import.meta.url, which cannot be mapped to a filesystem path.

Source

Thrown at ext/node/polyfills/url.ts:1378

 * @see https://www.rfc-editor.org/rfc/rfc3490#section-4
 */
function domainToUnicode(domain: string) {
  return idnaToUnicode(domain);
}

/**
 * This function ensures the correct decodings of percent-encoded characters as well as ensuring a cross-platform valid absolute path string.
 * @see Tested in `parallel/test-fileurltopath.js`.
 * @param path The file URL string or URL object to convert to a path.
 * @returns The fully-resolved platform-specific Node.js file path.
 */
function fileURLToPath(path: string | URL): string {
  if (typeof path === "string") path = new URL(path);
  else if (!ObjectPrototypeIsPrototypeOf(URL.prototype, path)) {
    throw new ERR_INVALID_ARG_TYPE("path", ["string", "URL"], path);
  }
  if (path.protocol !== "file:") {
    throw new ERR_INVALID_URL_SCHEME("file");
  }
  return isWindows ? getPathFromURLWin(path) : getPathFromURLPosix(path);
}

// https://url.spec.whatwg.org/#percent-decode
function isHexCharByte(byte: number): boolean {
  // 0-9 A-F a-f
  return (byte >= 0x30 && byte <= 0x39) || (byte >= 0x41 && byte <= 0x46) ||
    (byte >= 0x61 && byte <= 0x66);
}

function hexByteToNumber(byte: number): number {
  return (
    // 0-9
    byte >= 0x30 && byte <= 0x39
      ? (byte - 48)
      // Convert to uppercase: ((byte & 0xDF) - 65) + 10
      : ((byte & 0xDF) - 55)

View on GitHub (pinned to 9ad36f7a2c)

Solutions

  1. Branch on protocol: `if (new URL(u).protocol === 'file:')` convert, else handle remotely
  2. For remote modules, fetch() assets instead of reading the filesystem
  3. Use `import.meta.url.startsWith('file:')` as a cheap pre-check before deriving paths

Example fix

// before
const dir = path.dirname(fileURLToPath(import.meta.url)); // throws for https modules

// after
const dir = import.meta.url.startsWith("file:")
  ? path.dirname(fileURLToPath(import.meta.url))
  : "."; // remote module: fall back to cwd or fetch assets
Defensive patterns

Strategy: validation

Validate before calling

const u = typeof input === "string" ? new URL(input) : input;
if (u.protocol !== "file:") {
  throw new Error(`expected a file: URL, got ${u.protocol}`);
}
return fileURLToPath(u);

Type guard

const isFileUrl = (v: string | URL): boolean =>
  (typeof v === "string" ? new URL(v) : v).protocol === "file:";

Try / catch

try {
  p = fileURLToPath(import.meta.url);
} catch (e: any) {
  if (e?.code === "ERR_INVALID_URL_SCHEME") {
    p = "."; // remote module: no local file, fall back to cwd or fetch
  } else {
    throw e;
  }
}

Prevention

When it happens

Trigger: `fileURLToPath('https://example.com/mod.ts')`; `fileURLToPath(import.meta.url)` inside a remotely imported module; passing data: or http: URLs.

Common situations: Deno scripts importing from the network; code shared between local and remote module execution; build tools that feed bundle or asset URLs into path helpers.

Related errors


AI-assisted analysis of denoland/deno@9ad36f7a2c (2026-08-20). Data as JSON: /api/errors/bfe7838027440c42. Report an issue: GitHub.