vitest-dev/vitest · error · Error
process.exit unexpectedly called with "${code}"
Error message
process.exit unexpectedly called with "${code}" What it means
Identical guard to the base pool (error 382) but installed in runVmTests (vm.ts:114-116) for the VM pool: process.exit is overridden to throw so code under test cannot terminate the VM worker. The thrown Error reports the exit code that was requested.
Source
Thrown at packages/vitest/src/runtime/workers/vm.ts:115
// TODO: don't hardcode setImmediate in fake timers defaults
context.setImmediate = setImmediate
context.clearImmediate = clearImmediate
const stubs = getDefaultRequestStubs(context)
const externalModulesExecutor = new ExternalModulesExecutor({
context,
fileMap,
codeCache,
resolveCache,
moduleInfoCache,
packageCache,
transform: rpc.transform,
viteClientModule: stubs['/@vite/client'],
})
process.exit = (code = process.exitCode || 0): never => {
throw new Error(`process.exit unexpectedly called with "${code}"`)
}
listenForErrors(() => state)
const moduleRunner = startVitestModuleRunner({
context,
evaluatedModules: state.evaluatedModules,
state,
externalModulesExecutor,
createImportMeta: createNodeImportMeta,
traces,
})
emitModuleRunner(moduleRunner as any)
Object.defineProperty(context, VITEST_VM_CONTEXT_SYMBOL, {
value: {
context,View on GitHub (pinned to d568f8ce37)
Solutions
- Spy on process.exit (vi.spyOn(process,'exit').mockImplementation(...)) so it no-ops or throws an assertion-friendly error.
- Refactor the code under test to throw instead of exit.
- Mock the offending dependency with vi.mock if it cannot be changed.
Example fix
// before: process.exit in code under test
function fail(msg) { console.error(msg); process.exit(1) }
// after: throw, or stub in the test
function fail(msg) { throw new Error(msg) }
// test alternative
const exit = vi.spyOn(process, 'exit').mockImplementation((c) => { throw new Error(`exit ${c}`) }) Defensive patterns
Strategy: try-catch
Try / catch
const exitSpy = vi.spyOn(process, 'exit')
.mockImplementation((code?: number) => {
throw new Error(`process.exit(${code}) called in vm pool`)
})
try {
await runTestInVm()
} catch (e) {
expect((e as Error).message).toMatch(/process\.exit/)
} finally {
exitSpy.mockRestore()
} Prevention
- Treat process.exit as an error in library code — throw instead.
- In vm-pool tests, spy on process.exit before invoking code that might exit.
- Gate process.exit behind an isMainThread/shouldExit check so tests are unaffected.
When it happens
Trigger: Test code or an imported module calls process.exit() while running under pool 'vmThreads' or 'vmForks'. The override is installed after the VM context is set up and stays active for the run.
Common situations: Testing CLI/error-path code that hard-exits; a dependency that calls process.exit on a deprecation warning or missing native binding; test helpers that call process.exit to signal failure.
Related errors
- process.exit unexpectedly called with "${code}"
- Isolated tasks should not share runners
- [vitest] Failed to transform ${fileUrl}. Does the file exist
- Environment "${ctx.environment.name}" is not a valid environ
- Environment ${environment.name} doesn't provide "getVmContex
AI-assisted analysis of vitest-dev/vitest@d568f8ce37 (2026-08-03).
Data as JSON: /data/errors/e9fc5fb19750469d.json.
Report an issue: GitHub.