vitest-dev/vitest · error · Error
Pool " " cannot run with "experimental.viteModuleRunner…
Error message
Pool "${context.pool}" cannot run with "experimental.viteModuleRunner: false". Please, use "threads" or "forks" instead. What it means
setupVmWorker is the entry for the vm pool. It hard-requires experimental.viteModuleRunner to NOT be false, because the vm pool is built on Vitest's own module runner. If a config sets experimental.viteModuleRunner:false while the pool is vm/vmThreads, it throws telling the user to switch to threads or forks instead.
Solutions
- Remove experimental.viteModuleRunner:false (or set it true/omit it) when using pool vm/vmThreads.
- If you must disable the vite module runner, switch the pool to threads or forks: test.pool='threads'.
- Review the experimental config block and align pool choice with the runner setting.
Example fix
// before
export default defineConfig({
test: { pool: 'vmThreads', experimental: { viteModuleRunner: false } }
})
// after
export default defineConfig({
test: { pool: 'threads', experimental: { viteModuleRunner: false } }
}) Defensive patterns
Strategy: validation
Validate before calling
function assertPoolRunnerCompatible(pool, experimental) {
if (pool.startsWith('vm') && experimental?.viteModuleRunner === false) {
throw new Error('vm pool requires viteModuleRunner; use threads/forks')
}
} Type guard
function configSupportsVmPool(cfg) {
return cfg.experimental?.viteModuleRunner !== false
} Prevention
- Never combine a vm pool with viteModuleRunner:false.
- When disabling the vite module runner, switch to threads or forks.
- Review experimental flags whenever changing the pool.
When it happens
Trigger: Configuration combines test.pool='vm' (or 'vmThreads') with test.experimental.viteModuleRunner=false. The vm pool cannot operate without the Vitest module runner, so this combination is rejected at worker setup.
Common situations: Disabling the vite module runner to fall back to Vite's native loading while forgetting that the vm pool depends on it; copy-pasting an experimental config block from a threads-based project into a vm-pool project.
Related errors
- Environment " " is not a valid environment. Path " "…
- Failed to load custom "defines
- "Istanbul" coverage provider is not compatible with…
- Runner is not supported. Test files: .
- vitest/browser can be imported only inside the Browser…
AI-assisted analysis of vitest-dev/vitest@1fa9837ec2 (2026-08-11).
Data as JSON: /api/errors/8cd0d6efe46db585.
Report an issue: GitHub.
Appendix: source
Thrown at packages/vitest/src/runtime/workers/vm.ts:193
}
finally {
await traces.$(
'vitest.runtime.environment.teardown',
() => vm.teardown?.(),
)
// unregisters the runner from Vite's `Error.prepareStackTrace` interceptor:
// its module-level cache holds `evaluatedModules` of every runner it has
// seen, which would otherwise keep each test file's entire module graph
// (and with it the vm context) alive for the lifetime of the worker
await moduleRunner.close()
stripDisposedContext(context, initialContextKeys)
setActiveVmExecutor(undefined)
}
}
export function setupVmWorker(context: WorkerSetupContext): void {
if (context.config.experimental.viteModuleRunner === false) {
throw new Error(`Pool "${context.pool}" cannot run with "experimental.viteModuleRunner: false". Please, use "threads" or "forks" instead.`)
}
// V8's isolate-level compilation cache keeps evaluated `vm.SourceTextModule`s
// (and everything their module state references) alive until a
// memory-pressure GC clears the cache, which in practice means every test
// file's world accumulates until the worker hits `vmMemoryLimit`. The
// compiled-code caching the flag disables is already covered by the
// worker's own script and code caches.
v8.setFlagsFromString('--no-compilation-cache')
}
View on GitHub (pinned to 1fa9837ec2)