Skip to content
Latchkey

Vitest "Failed to terminate worker" in CI

Vitest finished the tests but a pool worker would not stop, so it had to force-terminate the thread. The cause is an open handle the test never closed: a timer, an interval, a socket, or a promise that never resolves.

What this error means

Vitest prints "Failed to terminate worker while running ..." or "Terminating worker thread", and the run may hang or be flaky in CI. It often points at a specific test file that leaks a handle.

Vitest output
Error: Failed to terminate worker while running
/home/runner/work/app/src/queue.test.ts.

Tests closed successfully but something prevented the main process from exiting.

Common causes

A timer or interval was never cleared

The test starts setInterval or setTimeout and never clears it, so the worker has pending work and cannot exit.

An open socket or unsettled promise

A database connection, server, or fetch was opened but never closed, keeping the event loop alive in the worker.

How to fix it

Close handles in teardown

  1. Clear timers and intervals the test created.
  2. Close sockets, servers, and connections in afterEach/afterAll.
  3. Re-run so the worker exits without being force-terminated.
queue.test.ts
afterEach(() => {
  clearInterval(timer);
});
afterAll(async () => {
  await server.close();
});

Isolate to surface the leaking file

Run the suspected file alone with the forks pool to confirm which test leaks the handle.

Terminal
npx vitest run src/queue.test.ts --pool=forks

How to prevent it

  • Close every connection, server, and timer a test opens.
  • Use afterEach/afterAll to release resources deterministically.
  • Mock long-lived I/O so tests do not leak real handles.

Related guides

Tired of flaky CI? Latchkey auto-heals failed jobs and retries them for you. Start free →