jest "thrown: Exceeded timeout" with no stack trace in CI logs: how to get the real error
Recovers the real error hiding behind jest's 'thrown: Exceeded timeout' message when CI logs show no stack trace. Shows how to isolate the test, use --detectOpenHandles to find the hanging call, and fix the unsettled promise or missing await instead of just raising the timeout. Use when a jest test times out in CI with no useful stack. Not for timeouts caused by genuinely slow code, parallel worker contention alone, or non-jest runners.
TL;DR
The timeout is a symptom: somewhere in that test a promise never settles, so jest kills the test outside any frame that could carry a stack trace. Isolate the single failing test with -t and --runInBand, rerun with --detectOpenHandles to see which handle stayed open, and fix the missing await, the mock that never resolves, or the done callback that never fires. Fix the hang, not the timeout, and the real error (or a clean pass) comes back.
jest "thrown: Exceeded timeout" with no stack trace in CI logs: how to get the real errorSteps
- Reproduce it in isolation:
npx jest path/to/spec -t "exact test name" --runInBand. Expected: the same timeout fires with only one test in play, which rules out parallel-worker contention as the cause. - Rerun the isolated test with
--detectOpenHandles:npx jest path/to/spec -t "exact test name" --runInBand --detectOpenHandles. Expected: jest prints the open handle (a server, timer, or socket the test never closed), pointing at the hanging call. - Read the test line by line for a promise that never settles: an
awaitmissing on a promise-returning call, a mocked function whose fake promise never resolves, or adonecallback that is never invoked. Expected: you find the exact call that hangs. - Fix the hang: add the missing
await, make the mock resolve or reject, or invokedone(). Expected: the test finishes in milliseconds and either passes or fails with a real assertion error that carries a stack trace. - Restore the normal timeout and run the whole spec file again. Expected: no "thrown: Exceeded timeout" anywhere in the log.
Use this when
- CI logs show "thrown: Exceeded timeout" with no stack trace
- The timeout reproduces even when the test runs alone
- You suspect a hanging promise, an unresolved mock, or a forgotten
done()rather than slow code
Not for this skill when
- The test is simply slow and needs a legitimately longer timeout
- The timeout only happens under parallel load and vanishes with --runInBand (that is resource contention, not a hang)
- The failure is in a different runner (mocha, vitest) with its own timeout mechanics
Variant phrasings
- jest timeout with no stack trace in CI
- how to debug a jest test that hangs with no error
- jest Exceeded timeout but the test is not slow
Why it happens
Jest's timeout fires from its own timer, outside the test's call stack, so there is no frame to attach a stack trace to. The most common trigger is a promise that never settles: the test is not slow, it is stuck forever, and the timeout is the only thing that ends it. Raising the timeout just delays the same empty message.
Edge cases
- If
--detectOpenHandlesnames a handle from a shared setup file, the leak is in setup, not the test. Fix it there. - A mock that resolves on one code path but not another produces an intermittent hang; check every branch of the mock.
- If the hang only happens in CI, look for CI-only fakes (a service mock wired only under a CI env var) that never resolve.
Provenance
Resolved from the public thread: https://vectle.com/posts/pst_lgF9XWeV3d1GbgVdkVyo9g