denoland/deno · error · TypeError

Must be a file URL

Error message

Must be a file URL

What it means

pathFromURL (ext/web/00_infra.js:376) accepts either a plain path string or a URL object; if given a URL, its protocol must be file:. Any other scheme (http:, https:, data:, blob:) has no filesystem path representation, so Deno throws TypeError('Must be a file URL'). Plain strings fall through unchanged and never hit this check.

Solutions

  1. For remote content use fetch(url) and read the response body instead of fs APIs
  2. For local modules pass a file: URL derived from import.meta.url, e.g. new URL('./data.json', import.meta.url)
  3. Or pass a plain filesystem path string, which pathFromURL passes through without validation

Example fix

// before
const text = await Deno.readTextFile(new URL('https://example.com/data.json'));

// after
const res = await fetch('https://example.com/data.json');
const text = await res.text();
Defensive patterns

Strategy: validation

Validate before calling

if (
  typeof target !== "string" &&
  !(target instanceof URL && target.protocol === "file:")
) {
  throw new TypeError(`expected a path or file: URL, got ${String(target)}`);
}

Type guard

function isPathOrFileUrl(
  v: string | URL,
): v is string | URL {
  return typeof v === "string" || v.protocol === "file:";
}

Try / catch

try {
  data = Deno.readTextFile(target);
} catch (e) {
  if (e instanceof TypeError && e.message === "Must be a file URL") {
    // remote or blob URL: fetch instead
    data = (await fetch(target)).text();
  } else {
    throw e;
  }
}

Prevention

When it happens

Trigger: Deno.readTextFile(new URL('https://example.com/data.json')); passing import.meta.url of a remote (http/https) module to an fs API; passing a blob: or data: URL where a path or file URL is expected.

Common situations: Assuming import.meta.url always works with fs APIs (only true for locally loaded file: modules); a variable that is sometimes a remote URL and sometimes a path; fetch-then-read code passing the wrong variable to readFileSync.

Related errors


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

Appendix: source

Thrown at ext/web/00_infra.js:376

// Keep in sync with `fromFileUrl()` in `std/path/posix.ts`.
/**
 * @param {URL} url
 * @returns {string}
 */
function pathFromURLPosix(url) {
  if (url.hostname !== "") {
    throw new TypeError("Host must be empty");
  }

  return decodeURIComponent(
    StringPrototypeReplace(url.pathname, PERCENT_RE, "%25"),
  );
}

function pathFromURL(pathOrUrl) {
  if (ObjectPrototypeIsPrototypeOf(URLPrototype, pathOrUrl)) {
    if (pathOrUrl.protocol != "file:") {
      throw new TypeError("Must be a file URL");
    }

    return core.build.os == "windows"
      ? pathFromURLWin32(pathOrUrl)
      : pathFromURLPosix(pathOrUrl);
  }
  return pathOrUrl;
}

// NOTE(bartlomieju): this is exposed on `internals` so we can test
// it in unit tests
internals.pathFromURL = pathFromURL;

// deno-lint-ignore deno-internal/prefer-primordials
const SymbolMetadata = Symbol.metadata ?? Symbol("Symbol.metadata");

return {
  ASCII_ALPHA,

View on GitHub (pinned to 9ad36f7a2c)