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

  1. Switch to modern fake timers (`jest.useFakeTimers()`) which give finer-grained control.
  2. Use `jest.runOnlyPendingTimers()` to drain only the current batch without recursing into newly scheduled ones.
  3. Fix the recursive `setImmediate` in application code to terminate.
  4. 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

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


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)