denoland/deno · error · Error

Empty filepath.

Error message

Empty filepath.

What it means

pathDirname is an internal helper of Deno's CommonJS compat layer (ext/node/polyfills/01_require.js); it throws this generic Error when the filepath handed to it is null or undefined. Note the asymmetry: an empty string returns '.', so only nullish values throw. End users normally hit it when CJS module resolution produced no filename - a malformed require target or a gap in the polyfill machinery - not through a public API they called directly.

Source

Thrown at ext/node/polyfills/01_require.js:511

}
setupBuiltinModules();

// Loading node:module has to bootstrap node:process. `_next_tick.ts`'s
// `nextTick()` returns without queueing anything until `enableNextTick()` runs,
// and that only happens inside `__bootstrapNodeProcess()` (node:process's
// deferred trigger). Anything reaching `process.nextTick` before then - e.g.
// `_events.mjs`'s `addCatch`, which routes a rejected handler promise to the
// `error` event under `captureRejections` - silently loses its callback.
//
// This used to happen by accident: the eager `vm.js` load above did
// `createLazyLoader("node:process")()` at its module body. Now that the builtin
// map is lazy, say it on purpose. node:process's closure is ~7 modules and
// every CommonJS entry point pulls it in anyway.
core.createLazyLoader("node:process")();

function pathDirname(filepath) {
  if (filepath == null) {
    throw new Error("Empty filepath.");
  } else if (filepath === "") {
    return ".";
  }
  return op_require_path_dirname(filepath);
}

function pathResolve(...args) {
  return op_require_path_resolve(args);
}

const nativeModulePolyfill = new SafeMap();

const relativeResolveCache = ObjectCreate(null);
let requireDepth = 0;
let statCache = null;
let mainModule = null;
let hasBrokenOnInspectBrk = false;
let hasInspectBrk = false;

View on GitHub (pinned to 9ad36f7a2c)

Solutions

  1. Upgrade Deno - node/require interop fixes land in nearly every release
  2. Reproduce with a minimal require('the-package') and confirm the specifier is a non-empty, valid string
  3. If it originates from your own module hooks, make sure resolve hooks always return a real URL and load hooks a real filename

Example fix

// before (custom hook returning a malformed result)
registerHooks({
  resolve(spec, ctx, next) {
    if (spec.startsWith('virtual:')) return; // undefined filename leaks downstream
    return next(spec, ctx);
  },
});

// after
registerHooks({
  resolve(spec, ctx, next) {
    if (spec.startsWith('virtual:')) {
      return { shortCircuit: true, url: pathToFileURL(spec.slice(8)).href };
    }
    return next(spec, ctx);
  },
});
Defensive patterns

Strategy: try-catch

Validate before calling

function requireSafe(id: string): unknown {
  if (typeof id !== 'string' || id.length === 0) {
    throw new TypeError(`require() needs a non-empty specifier, got: ${String(id)}`);
  }
  return require(id);
}

Type guard

function isNonEmptyPath(p: unknown): p is string {
  return typeof p === 'string' && p.length > 0;
}

Try / catch

try {
  const mod = require(specifier);
} catch (e) {
  if (e instanceof Error && e.message === 'Empty filepath.') {
    // internal CJS resolution produced no filename: log specifier and report upstream
    throw new Error(`module resolution returned no path for '${specifier}' (Deno CJS interop)`);
  }
  throw e;
}

Prevention

When it happens

Trigger: A require() whose specifier resolves to a nullish filename inside the CJS loader; custom resolve/load hooks returning malformed results that leave filename unset; interplay between the eager builtin-map loading and a package expecting a path.

Common situations: Bundled npm packages exercising corner cases of Deno's require polyfill; registerHooks-based loaders returning bad URLs; older Deno versions with resolution gaps that were fixed later.

Related errors


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