jestjs/jest · error · Error
Ran immediates, and there are still more! Assuming we've…
Error message
Ran ${this._maxLoops} immediates, and there are still more! Assuming we've hit an infinite recursion and bailing out... What it means
Thrown by legacy fake timers' `runAllImmediates()` after exhausting `_maxLoops` iterations (default 100,000). The method drains `setImmediate` callbacks; if each immediate schedules another, the queue never drains and Jest aborts to prevent an infinite recursion.
Solutions
- Switch to modern fake timers (`jest.useFakeTimers()`) which give finer-grained control.
- Use `jest.runOnlyPendingTimers()` to drain only the current batch without recursing into newly scheduled ones.
- Fix the recursive `setImmediate` in application code to terminate.
- Mock the source of the recurring scheduling in the test.
Example fix
// before
jest.useFakeTimers({ legacyFakeTimers: true });
function work() { setImmediate(work); }
work();
jest.runAllTimers(); // throws via runAllImmediates
// after
jest.useFakeTimers();
work();
jest.runOnlyPendingTimers(); // drains only current batch Defensive patterns
Strategy: validation
Validate before calling
// Check for active setImmediate loops before runAllTimers
function hasActiveImmediates(fakeTimers) {
return fakeTimers.getTimerCount() > 0;
} Try / catch
try {
jest.runAllTimers();
} catch (e) {
if (e.message.includes('infinite recursion')) {
jest.runOnlyPendingTimers(); // fallback
} else { throw e; }
} Prevention
- Use modern fake timers to avoid the legacy runAllImmediates path entirely.
- Use runOnlyPendingTimers for code with recursive setImmediate.
- Clear all immediates with clearAllTimers before re-running if needed.
When it happens
Trigger: Using legacy fake timers and calling `jest.runAllTimers()` (which internally calls `runAllImmediates()`) when code recursively calls `setImmediate` within an immediate callback without a break condition.
Common situations: Application code using `setImmediate` for a scheduling loop (e.g. a work queue that keeps re-enqueuing). A streaming data consumer that calls setImmediate to yield but the stream never ends in the test.
Related errors
- Ran ticks, and there are still more! Assuming we've hit an…
- Ran timers, and there are still more! Assuming we've hit an…
AI-assisted analysis of jestjs/jest@8e6d128e4a (2026-08-10).
Data as JSON: /api/errors/00dab8fd476d8a72.
Report an issue: GitHub.
Appendix: source
Thrown at packages/jest-fake-timers/src/legacyFakeTimers.ts:190
"Assuming we've hit an infinite recursion and bailing out...",
);
}
}
runAllImmediates(): void {
this._checkFakeTimers();
// Only run a generous number of immediates and then bail.
let i;
for (i = 0; i < this._maxLoops; i++) {
const immediate = this._immediates.shift();
if (immediate === undefined) {
break;
}
this._runImmediate(immediate);
}
if (i === this._maxLoops) {
throw new Error(
`Ran ${this._maxLoops} immediates, and there are still more! Assuming ` +
"we've hit an infinite recursion and bailing out...",
);
}
}
private _runImmediate(immediate: Tick) {
try {
immediate.callback();
} finally {
this._fakeClearImmediate(immediate.uuid);
}
}
runAllTimers(): void {
this._checkFakeTimers();
this.runAllTicks();
this.runAllImmediates();View on GitHub (pinned to 8e6d128e4a)