denoland/deno · error · InvalidURLSchemeError

ERR_INVALID_URL_SCHEME

ERR_INVALID_URL_SCHEME

Error message

The URL must be of scheme file:

What it means

node:sqlite's DatabaseSync (and backup()) accept the database location as a string, a Uint8Array, or a URL object — but a URL is only meaningful with the `file:` scheme. parsePath checks `path.protocol !== 'file:'` and throws ERR_INVALID_URL_SCHEME for anything else, because sqlite only opens local files.

Source

Thrown at ext/node/polyfills/sqlite.ts:136

class InvalidStateError extends Error {
  code;
  constructor(message) {
    super(message);
    this.code = "ERR_INVALID_STATE";
  }
}

const parsePath = (path) => {
  let parsedPath;
  if (typeof path === "string") {
    parsedPath = path;
  } else if (isUint8Array(path)) {
    const decoder = new TextDecoder("utf8");
    parsedPath = decoder.decode(path);
  } else if (ObjectPrototypeIsPrototypeOf(URLPrototype, path)) {
    if (path.protocol !== "file:") {
      throw new InvalidURLSchemeError();
    }
    parsedPath = path.href;
  }

  if (
    typeof parsedPath === "undefined" ||
    StringPrototypeIncludes(parsedPath, "\0")
  ) {
    throw new InvalidArgTypeError(
      'The "path" argument must be a string, Uint8Array, or URL without null bytes.',
    );
  }

  return parsedPath;
};

// Using ES5 class allows custom error to be thrown
// when called without `new`.

View on GitHub (pinned to 9ad36f7a2c)

Solutions

  1. Pass a plain filesystem path string instead of a URL
  2. If you have a URL, keep the file: scheme (`new URL('file:///data/app.db')`) or extract `url.pathname`
  3. For remote databases, copy/download the file locally first — node:sqlite cannot open remote resources

Example fix

// before
const db = new DatabaseSync(new URL('https://example.com/db.sqlite'));
// ERR_INVALID_URL_SCHEME

// after
const db = new DatabaseSync('/data/db.sqlite');
// or
const db2 = new DatabaseSync(new URL('file:///data/db.sqlite'));
Defensive patterns

Strategy: type-guard

Validate before calling

function toSqlitePath(p) {
  if (p instanceof URL) {
    if (p.protocol !== 'file:') {
      throw new TypeError(`node:sqlite requires a file: URL, got ${p.protocol}`);
    }
    return decodeURIComponent(p.pathname);
  }
  return p; // string or Uint8Array pass through
}
const db = new DatabaseSync(toSqlitePath(loc));

Type guard

const isFileUrl = (u) => u instanceof URL && u.protocol === 'file:';

Prevention

When it happens

Trigger: `new DatabaseSync(new URL('http://example.com/db.sqlite'))`, `new DatabaseSync(new URL('sqlite:foo'))`, or any non-file URL passed as the first argument (also as the `path` argument to `backup(db, url)`).

Common situations: Config-driven apps that build a single URL and pass it to both fetch() and sqlite; 'upgrading' a plain path to a URL during a better-sqlite3 migration; remote DSN strings (postgres://, https://) reused for a local cache DB.

Related errors


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