vitest-dev/vitest · critical · Error

Expected worker to be run in node:worker_threads

Error message

Expected worker to be run in node:worker_threads

What it means

Module-level guard in init-threads.ts (line 6-8): the threads worker entrypoint requires both isMainThread === false and a non-null parentPort, i.e. it must run inside a node:worker_threads Worker. Importing it in the main thread or a child process throws at import time.

Source

Thrown at packages/vitest/src/runtime/workers/init-threads.ts:7

import type { WorkerGlobalState, WorkerSetupContext } from '../../types/worker'
import type { Traces } from '../../utils/traces'
import { isMainThread, parentPort } from 'node:worker_threads'
import { init } from './init'

if (isMainThread || !parentPort) {
  throw new Error('Expected worker to be run in node:worker_threads')
}

export default function workerInit(options: {
  runTests: (method: 'run' | 'collect', state: WorkerGlobalState, traces: Traces) => Promise<void>
  setup?: (context: WorkerSetupContext) => void | Promise<() => Promise<unknown>>
}): void {
  const { runTests } = options

  init({
    post: response => parentPort!.postMessage(response),
    on: callback => parentPort!.on('message', callback),
    off: callback => parentPort!.off('message', callback),
    teardown: () => parentPort!.removeAllListeners('message'),
    runTests: async (state, traces) => runTests('run', state, traces),
    collectTests: async (state, traces) => runTests('collect', state, traces),
    setup: options.setup,
  })
}

View on GitHub (pinned to d568f8ce37)

Solutions

  1. Do not import init-threads directly; let Vitest spawn the worker via pool: 'threads'.
  2. If building a custom pool, load the threads entry only inside `new Worker(...)`.
  3. Switch to pool: 'forks' if you intended to use child processes.

Example fix

// before: importing threads entry in main thread
import workerInit from 'vitest/dist/workers/threads.js'

// after: configure vitest to spawn the worker
// vitest.config.ts
export default defineConfig({ test: { pool: 'threads' } })
Defensive patterns

Strategy: validation

Validate before calling

import { isMainThread, parentPort } from 'node:worker_threads'

function assertWorkerThreadContext() {
  if (isMainThread || !parentPort) {
    throw new Error(
      'init-threads must be loaded inside a Worker (worker_threads); do not import it from the main thread.'
    )
  }
}

Prevention

When it happens

Trigger: Importing the threads worker entry (init-threads.ts or the built runThreadsTests file) from the main thread, a forked child_process, or any non-worker-thread context where isMainThread is true or parentPort is null.

Common situations: A custom pool pointing at the threads entry but spawning a fork; tests or scripts that import internal vitest worker modules; experimentation with the worker init code outside a Worker.

Related errors


AI-assisted analysis of vitest-dev/vitest@d568f8ce37 (2026-08-03). Data as JSON: /data/errors/dce5a470f6421f0a.json. Report an issue: GitHub.