Vitest Unhandled Rejection Fails the Suite in CI
Vitest failed the run because a promise rejected outside any awaited test - an unhandled rejection. The work was fired-and-forgotten, so its rejection surfaced after the test that started it, failing the whole file.
What this error means
The run reports "Unhandled Rejection" (or "Vitest caught N unhandled errors during the test run") even though individual tests show as passed. The stack often points at a setTimeout, a floating promise, or teardown.
Unhandled Rejection
Error: connect ECONNREFUSED 127.0.0.1:5432
❯ Timeout._onTimeout src/poller.ts:14:11
This error originated in "src/poller.test.ts". It was caught after the
test had already finished.Common causes
A floating (unawaited) promise rejected
A test started async work without awaiting it. The test ended green, then the promise rejected with no handler, so Vitest flags an unhandled rejection.
Background timer/poller not stopped
A setInterval/poller created in a test keeps running after teardown and eventually rejects (e.g. a closed connection), failing a later or already-finished test.
How to fix it
Await all async work
Return or await every promise so its rejection is attached to the test, not the global handler.
it('polls once', async () => {
await expect(pollOnce()).resolves.toBeDefined();
});Stop background work in teardown
let timer;
beforeEach(() => { timer = startPoller(); });
afterEach(() => { clearInterval(timer); });How to prevent it
- Never fire-and-forget promises in tests; await them.
- Clear timers/intervals and close connections in
afterEach. - Use fake timers to make time-based code deterministic.