denoland/deno · error · Error
ERR_VM_MODULE_DIFFERENT_CONTEXT
ERR_VM_MODULE_DIFFERENT_CONTEXT
Error message
Linked modules must use the same context
What it means
Thrown by vm.Module#link() when the linker returns a module that was created against a different context than the module being linked. Every module records the `context` from its options object in kContext; link() enforces `m.context === this[kContext]` (vm.js:682) because a V8 module graph must be instantiated within a single context.
Source
Thrown at ext/node/polyfills/vm.js:683
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 {
validateObject(options, "options");
const status = op_vm_module_get_status(this[kWrap]);
// Allow evaluate from linked (2), evaluating (3), evaluated (4), errored (5).View on GitHub (pinned to 9ad36f7a2c)
Solutions
- Pass the exact same context value to every module in the graph: derive it inside the linker from `mod.context` (the second link() argument)
- If the root module has no context, omit `context` in dependency module options too (undefined must match undefined)
- Key any linker-side module cache by `${specifier}\0${context ?? 'default'}` so modules are never reused across contexts
- Re-create cached dependency modules when the incoming module's context differs from the cached one
Example fix
// before
const ctx = vm.createContext({});
const root = new vm.SourceTextModule(code); // no context
root.link(async (spec) => new vm.SourceTextModule(files[spec], { context: ctx })); // ERR_VM_MODULE_DIFFERENT_CONTEXT
// after
const ctx = vm.createContext({});
const root = new vm.SourceTextModule(code, { context: ctx });
root.link(async (spec, mod) =>
new vm.SourceTextModule(files[spec], { context: mod.context })); Defensive patterns
Strategy: type-guard
Validate before calling
const deps = await Promise.all(requests.map((r) => linker(r.specifier, mod)));
for (const d of deps) {
if (d.context !== mod.context) throw new Error(`context mismatch for ${d.identifier}`);
} Type guard
const sameContext = (a, b) => a.context === b.context; // undefined must match undefined
Try / catch
mod.link(linker).catch((e) => {
if (e.code === 'ERR_VM_MODULE_DIFFERENT_CONTEXT') {
// rebuild dependency modules with mod.context, then retry with a FRESH root module
} else throw e;
}); Prevention
- Create the context once per sandbox and thread it to every module constructor
- Inside linkers, read the requesting module's context via the second callback argument
- Key linker caches by (specifier, context) pairs
When it happens
Trigger: Creating the root module with `new vm.SourceTextModule(code)` (no context) but returning dependencies built with `new vm.SourceTextModule(code, { context: someContext })`, or vice versa; using two different context objects from two vm.createContext() calls for root and dependencies; sharing a cached module created for context A while linking a graph rooted in context B.
Common situations: A linker that caches modules in a Map keyed only by specifier, ignoring which context they were created for, then reuses that cache for a second run under a fresh vm.createContext(); tutorials that create the dependency inside `vm.createContext(...)` while the root module is context-less; refactoring code so only some modules receive options.context.
Related errors
- ERR_VM_MODULE_NOT_MODULE
- ERR_MODULE_LINK_MISMATCH
- ERR_INVALID_ARG_VALUE
- Empty filepath.
- resolve hook must return { shortCircuit: true } or call next
AI-assisted analysis of denoland/deno@9ad36f7a2c (2026-08-20).
Data as JSON: /api/errors/e8acb954584a6ce8.
Report an issue: GitHub.