denoland/deno · error · Error

mock exports not found for "${key}"

Error message

mock exports not found for "${key}"

What it means

mock.module() registers the fake on a global registry (globalThis[Symbol.for('deno.internal.nodeTestMockModules')]) keyed by resolved specifier, and installs a generated CommonJS shim that looks the entry up at require() time. If the registry has no entry for that key when the shim runs, it throws this error. The entry is deleted on restore/reset, so the usual cause is requiring the module after the mock was torn down, or a specifier resolving to a different key.

Source

Thrown at ext/node/polyfills/testing.ts:3173

  if (
    StringPrototypeStartsWith(key, "http:") ||
    StringPrototypeStartsWith(key, "https:")
  ) {
    return "module";
  }
  return "commonjs";
}

function registryAccessSource() {
  return "globalThis[Symbol.for(" + JSONStringify(kMockModuleRegistryName) +
    ")]";
}

function generateCjsSource(entry, key) {
  let src = '"use strict";\n';
  src += "const $e = " + registryAccessSource() + ".get(" +
    JSONStringify(key) + ");\n";
  src += "if ($e === undefined) { throw new Error(" +
    JSONStringify('mock exports not found for "' + key + '"') + "); }\n";
  if (entry.hasDefaultExport) {
    src += "module.exports = $e.moduleExports.default;\n";
  }
  if (entry.exportNames.length > 0) {
    src += "if (module.exports === null || typeof module.exports !== " +
      '"object") { throw new Error(' + JSONStringify(kBadExportsMessage) +
      "); }\n";
    for (let i = 0; i < entry.exportNames.length; i++) {
      const name = entry.exportNames[i];
      src += "module.exports[" + JSONStringify(name) + "] = " +
        "$e.moduleExports[" + JSONStringify(name) + "];\n";
    }
  }
  return src;
}

function generateEsmSource(entry, key) {

View on GitHub (pinned to 9ad36f7a2c)

Solutions

  1. Keep the mock active for the whole lifetime of the module instance - restore only in afterEach, after the test has finished requiring and using it
  2. Mock the exact specifier the code under test requires so both resolve to the same key
  3. Require the module inside the test body after mock.module(), not at top level before mocks exist
  4. Re-register with mock.module() before requiring again in a later test

Example fix

// before
m.restore();
const pkg = require('pkg'); // shim throws: mock exports not found

// after
// keep the mock active until the test is done
const pkg = require('pkg');
m.restore();
Defensive patterns

Strategy: validation

Validate before calling

const REGISTRY = Symbol.for('deno.internal.nodeTestMockModules');
function isModuleMocked(spec: string): boolean {
  const reg = (globalThis as Record<symbol, Map<string, unknown> | undefined>)[REGISTRY];
  return reg?.get(spec) !== undefined; // internal key: resolution must match
}

Try / catch

try { require('pkg'); } catch (e) { if (/mock exports not found/.test(e.message)) { throw new Error('pkg required outside its mock lifetime'); } throw e; }

Prevention

When it happens

Trigger: const m = mock.module('pkg'); m.restore(); require('pkg') afterwards; a lazily-required dependency executing after afterEach ran restoreAllMocks; mocking './util' while the code under test requires a path that resolves to a different key.

Common situations: Teardown ordering where a deferred require runs after afterEach; specifier spelling/relative-path mismatches between the mock call and the required id; cached compiled shims outliving their registry entry.

Related errors


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