santifer/career-ops · error · TypeError

isMainModule expects import.meta.url; got ${typeof moduleUrl

Error message

isMainModule expects import.meta.url; got ${typeof moduleUrl === 'string' ? 'an empty string' : typeof moduleUrl}

What it means

isMainModule() answers 'was this file the entry point node ran?' and it can only answer that from the module's `import.meta.url`, a `file:` URL string. The function rejects non-string or empty-string inputs with a TypeError at the very first check, before any comparison, because such a value cannot identify a module. This guards against callers passing undefined, null, or accidentally shadowed variables.

Source

Thrown at lib/is-main-module.mjs:81

 */
const canonicalize = realpathSync.native ?? realpathSync;

/**
 * True when the module identified by `moduleUrl` is the process entrypoint.
 *
 * Call it as `isMainModule(import.meta.url)`. Returns false when the module was
 * imported rather than run, which is what keeps a CLI tail from firing inside
 * `node --test`, `test-all.mjs`, or any script that imports the module's
 * exported functions.
 *
 * @param {string} moduleUrl - The caller's `import.meta.url`. A `file:` URL, and
 *   deliberately nothing else — see the throw below.
 * @returns {boolean} True when this module is what `node` was pointed at.
 * @throws {TypeError} When handed a filesystem path instead of a `file:` URL.
 */
export function isMainModule(moduleUrl) {
  if (typeof moduleUrl !== 'string' || moduleUrl === '') {
    throw new TypeError(`isMainModule expects import.meta.url; got ${typeof moduleUrl === 'string' ? 'an empty string' : typeof moduleUrl}`);
  }

  if (!moduleUrl.startsWith('file:')) {
    // A Windows drive letter parses as a one-character URL scheme, so it has to
    // be excluded before the scheme test or `C:\co\pdf.mjs` reads as a URL.
    //
    // Matched WITHOUT requiring a following separator, because `C:repo\pdf.mjs`
    // is also a path — the drive-RELATIVE form, resolved against the current
    // directory on C:. Requiring `[\\/]` let that one through to the `return
    // false` below, which is the silent-suppression footgun this branch exists
    // to prevent. No registered URL scheme is a single letter, so treating
    // `X:` as a drive is unambiguous.
    const isPath = !/^[a-zA-Z][a-zA-Z0-9+.-]*:/.test(moduleUrl) || /^[a-zA-Z]:/.test(moduleUrl);
    if (isPath) {
      // LOUD, because the quiet alternative is the bug this module exists to
      // kill. `isMainModule(import.meta.filename)` would resolve, compare
      // false, skip the CLI tail, and exit 0 having printed nothing — #3170
      // reintroduced one argument at a time. A crash names the mistake instead.

View on GitHub (pinned to 1696bec4d0)

Solutions

  1. Pass `import.meta.url` directly: `if (isMainModule(import.meta.url)) {...}`
  2. If in a CommonJS file, don't use this helper — use `require.main === module` instead
  3. If a build tool empties import.meta.url, configure it to preserve ESM metadata (e.g. emit ESM output)

Example fix

// before
if (isMainModule(import.meta.filename)) { cli(); }
// after
if (isMainModule(import.meta.url)) { cli(); }
Defensive patterns

Strategy: type-guard

Validate before calling

if (typeof import.meta.url === 'string' && import.meta.url !== '') { isMain(import.meta.url); }

Type guard

const isModuleUrl = (v) => typeof v === 'string' && v.startsWith('file:');

Try / catch

try { if (isMainModule(import.meta.url)) cli(); } catch (e) { if (e instanceof TypeError) console.error('Bad moduleUrl passed to isMainModule:', e.message); else throw e; }

Prevention

When it happens

Trigger: Calling isMainModule(undefined), isMainModule(null), isMainModule(someObject), or isMainModule('') — any argument that is not a non-empty string.

Common situations: Copy-pasting the call into CommonJS code where `import.meta` doesn't exist (so the variable is undefined); passing import.meta.filename or __filename instead of import.meta.url; a bundler/transpiler that rewrites import.meta.url to '' or drops it.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


AI-assisted analysis of santifer/career-ops@1696bec4d0 (2026-09-01). Data as JSON: /api/errors/7f8b07fbb2e6f52e. Report an issue: GitHub.