vitest-dev/vitest · error · Error
[vitest] Expected synchronous evaluation to complete for ${r
Error message
[vitest] Expected synchronous evaluation to complete for ${rootIdentifier}, but module status is "${root.module.status}". This is a bug in Vitest. What it means
Vitest's vm ESM executor asserts that a module graph containing no top-level await evaluates synchronously. After calling root.module.evaluate() it checks status: 'errored' rethrows the error, 'evaluated' is success, and any other status (e.g. 'evaluating', 'unlinked') trips this assertion. The message explicitly states it is a bug in Vitest, because with no async in the graph Node's vm SourceTextModule.evaluate() is contractually synchronous.
Source
Thrown at packages/vitest/src/runtime/vm/esm-executor.ts:302
: `its dependency uses top-level await (${culprit})`,
)
}
for (const [identifier, entry] of scratch) {
if (entry.commit && !this.moduleCache.has(identifier)) {
this.moduleCache.set(identifier, entry.module)
}
}
// with no top-level await in the graph, evaluate() fulfills synchronously
// and an evaluation error lands on `status`/`error`, not on the promise
root.module.evaluate().catch(() => {})
if (root.module.status === 'errored') {
throw root.module.error
}
if (root.module.status !== 'evaluated') {
throw new Error(
`[vitest] Expected synchronous evaluation to complete for ${rootIdentifier}, but module status is "${root.module.status}". This is a bug in Vitest.`,
)
}
return root.module
}
// A cached module is reusable by the sync walker only when it is settled:
// anything else (a pending Promise or a module in 'unlinked' → 'evaluating')
// is a concurrent import() mid-flight that a synchronous require() can
// neither await nor safely link against.
private reuseSyncModule(
identifier: string,
cached: VMModule | Promise<VMModule>,
): VMModule {
if (cached instanceof Promise) {
throw createConcurrentRequireError(identifier)
}
if (cached.status === 'errored') {View on GitHub (pinned to 1fa9837ec2)
Solutions
- Report it as a Vitest bug with the full module graph and the module that triggered the require(esm); include Vitest, Node, and OS versions.
- Check whether any dependency in the import graph newly introduced top-level await (e.g. after an npm update) and refactor or mock it.
- Try a different pool (threads/forks instead of vm) to confirm it is vm-executor specific: test.pool='threads'.
- Downgrade Node.js to a known-good LTS to rule out a vm.SourceTextModule regression, or downgrade Vitest to the last working version.
Example fix
// before: test file imports a dep that gained top-level await
import 'some-dep' // some-dep now has `await` at top level
// after: avoid the sync require path by using dynamic import, or pin the dep
import('some-dep') Defensive patterns
Strategy: try-catch
Validate before calling
// Cannot be validated by callers reliably; it is an internal assertion.
// Best pre-check: confirm the graph is truly synchronous before require(esm).
const hasAsync = (m) => m instanceof SourceTextModule && m.hasAsyncGraph()
if (!hasAsync(root)) { /* safe to require */ } Type guard
function isEvaluated(m) {
return m.status === 'evaluated' || m.status === 'errored'
} Try / catch
try {
executor.requireSync(identifier)
} catch (e) {
if (String(e.message).includes('This is a bug in Vitest')) {
// file an issue; fall back to dynamic import
await import(identifier)
} else throw e
} Prevention
- Keep imported graphs free of top-level await when using require(esm) in the vm pool.
- Pin Node.js to an LTS version known to work with your Vitest version.
- Reproduce on threads/forks pool to rule out vm-executor-specific regressions before reporting.
When it happens
Trigger: A require(esm) import resolves a module whose hasAsyncGraph() reported false, yet root.module.evaluate() leaves status as anything other than 'errored' or 'evaluated'. Concretely this fires inside EsmExecutor when the sync module walker commits a graph it believed to be synchronous but Node disagrees, or when the module transitions states mid-evaluation due to re-entrant imports.
Common situations: Hitting an internal Vitest regression after upgrading Node.js (vm.SourceTextModule behavior changed), mixing require(esm) with modules that have hidden async (a dependency added top-level await), or a re-entrant dynamic import() racing the sync evaluation. Rare in stable releases; usually surfaces on nightly/edge Vitest or Node versions.
Related errors
- The VM environment was not defined in the Vite config. This
- Invalid data URI
- Missing data URI encoding
- Invalid data URI encoding: ${encoding}
- Cannot import "${mod.identifier}": its vm context was torn d
AI-assisted analysis of vitest-dev/vitest@1fa9837ec2 (2026-08-11).
Data as JSON: /api/errors/d75845866ef066e4.
Report an issue: GitHub.