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
- Pass `import.meta.url` directly: `if (isMainModule(import.meta.url)) {...}`
- If in a CommonJS file, don't use this helper — use `require.main === module` instead
- 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
- Always call it as isMainModule(import.meta.url), never with a variable holding filename/path
- Don't post-process import.meta.url (resolve, decode) before passing it
- In CommonJS files use require.main === module instead of this helper
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.