denoland/deno · error · Error

ERR_VM_MODULE_NOT_MODULE

ERR_VM_MODULE_NOT_MODULE

Error message

Provided module is not an instance of Module

What it means

Thrown by vm.Module#link() when a linker callback resolves with a value that is not a vm.SourceTextModule or vm.SyntheticModule instance. After awaiting all linker promises, the polyfill duck-checks each result with isModule() (a non-null object carrying the internal kWrap symbol, vm.js:554). Node's V8 binding raises the same code, so behavior matches node:vm.

Source

Thrown at ext/node/polyfills/vm.js:680

    }

    const specifiers = [];
    const linkerPromises = [];
    for (let i = 0; i < requests.length; i++) {
      const { specifier, attributes } = requests[i];
      ArrayPrototypePush(specifiers, specifier);
      const p = PromiseResolve(
        linker(specifier, this, { attributes, assert: attributes }),
      );
      ArrayPrototypePush(linkerPromises, p);
    }
    const resolvedModules = await SafePromiseAll(linkerPromises);

    const wraps = [];
    for (let i = 0; i < resolvedModules.length; i++) {
      const m = resolvedModules[i];
      if (!isModule(m)) {
        throw new ERR_VM_MODULE_NOT_MODULE();
      }
      if (m.context !== this[kContext]) {
        throw new ERR_VM_MODULE_DIFFERENT_CONTEXT();
      }
      ArrayPrototypePush(wraps, m[kWrap]);
    }

    // Record this module's resolutions before recursing so a dependency that
    // imports back into this module finds it already in `visited`.
    op_vm_module_link(this[kWrap], specifiers, wraps);

    for (let i = 0; i < resolvedModules.length; i++) {
      await resolvedModules[i][kLinkGraph](linker, visited);
    }
  }

  evaluate(options = { __proto__: null }) {
    try {

View on GitHub (pinned to 9ad36f7a2c)

Solutions

  1. Make the linker return a `new vm.SourceTextModule(loadedCode, { identifier: specifier })` or a `new vm.SyntheticModule([...], cb)` instance for every specifier it is given
  2. Check for a missing `return` in the linker arrow function — an implicit undefined is the most common cause
  3. For virtual/static dependencies, return a SyntheticModule that setExport()s the needed values
  4. Make sure only one copy of the node:vm polyfill creates modules (no duplicate vm module instances across realms/bundles)

Example fix

// before
mod.link(async (specifier) => {
  const code = fs.readFileSync(resolve(dir, specifier)); // returns Buffer -> ERR_VM_MODULE_NOT_MODULE
});
// after
mod.link(async (specifier) => {
  const code = fs.readFileSync(resolve(dir, specifier), 'utf8');
  return new vm.SourceTextModule(code, { identifier: specifier });
});
Defensive patterns

Strategy: type-guard

Validate before calling

const m = await linker(spec, mod, attrs);
if (!(m instanceof vm.SourceTextModule || m instanceof vm.SyntheticModule)) {
  throw new TypeError(`linker returned ${typeof m} for ${spec}; expected a vm module`);
}

Type guard

const isVmModule = (v) =>
  v instanceof vm.SourceTextModule || v instanceof vm.SyntheticModule;

Try / catch

mod.link(linker).catch((e) => {
  if (e.code === 'ERR_VM_MODULE_NOT_MODULE') {
    // inspect linker return values; log which specifier failed
  } else throw e;
});

Prevention

When it happens

Trigger: Calling `module.link(async (specifier) => ...)` where the callback returns a module namespace object (`import * as ns`), a source string, a Buffer/Uint8Array of code, undefined (missing return), a plain object like {exports}, or a module created by a different loaded copy of the vm polyfill so the internal kWrap symbol does not match.

Common situations: Writing a custom linker that loads files with fs.readFileSync(path) and returns the buffer or the file content instead of wrapping it in `new vm.SourceTextModule(code, {identifier: specifier})`; forgetting the `return` keyword in an arrow-function linker; returning `null` for dependencies the developer considers optional; returning `require(...)` results instead of vm module instances.

Related errors


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