{"record":{"id":"df2921930a4254d3","repo":"jestjs/jest","slug":"ran-this-maxloops-ticks-and-there-are-still-m","errorCode":null,"errorMessage":"Ran ${this._maxLoops} ticks, and there are still more! Assuming we've hit an infinite recursion and bailing out...","messagePattern":"Ran (.+?) ticks, and there are still more! Assuming we've hit an infinite recursion and bailing out\\.\\.\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/jest-fake-timers/src/legacyFakeTimers.ts","lineNumber":170,"sourceCode":"    let i;\n    for (i = 0; i < this._maxLoops; i++) {\n      const tick = this._ticks.shift();\n\n      if (tick === undefined) {\n        break;\n      }\n\n      if (\n        !Object.prototype.hasOwnProperty.call(this._cancelledTicks, tick.uuid)\n      ) {\n        // Callback may throw, so update the map prior calling.\n        this._cancelledTicks[tick.uuid] = true;\n        tick.callback();\n      }\n    }\n\n    if (i === this._maxLoops) {\n      throw new Error(\n        `Ran ${this._maxLoops} ticks, and there are still more! ` +\n          \"Assuming we've hit an infinite recursion and bailing out...\",\n      );\n    }\n  }\n\n  runAllImmediates(): void {\n    this._checkFakeTimers();\n    // Only run a generous number of immediates and then bail.\n    let i;\n    for (i = 0; i < this._maxLoops; i++) {\n      const immediate = this._immediates.shift();\n      if (immediate === undefined) {\n        break;\n      }\n      this._runImmediate(immediate);\n    }\n","sourceCodeStart":152,"sourceCodeEnd":188,"githubUrl":"https://github.com/jestjs/jest/blob/8e6d128e4a278059ecddecaa97400b04c8ae5fd9/packages/jest-fake-timers/src/legacyFakeTimers.ts#L152-L188","documentation":"Thrown by legacy fake timers' `runAllTicks()` after exhausting `_maxLoops` iterations (default 100,000). The method drains `process.nextTick` callbacks; if each tick schedules another tick, the queue never empties and Jest bails out to avoid a true infinite loop.","triggerScenarios":"Using `jest.useFakeTimers({ legacyFakeTimers: true })` and calling `jest.runAllTicks()` when application code recursively calls `process.nextTick` inside a nextTick callback. Each iteration shifts a tick and runs it, but the callback enqueues another, so the counter never reaches zero.","commonSituations":"Code that uses `process.nextTick` for a polling/retry loop without a termination condition. A recursive nextTick chain meant to yield control but never stops. Testing code with a real event-emitter that re-schedules on every tick.","solutions":["Switch from legacy to modern fake timers: `jest.useFakeTimers()` (without `legacyFakeTimers: true`) — sinon-backed timers handle this more gracefully.","Use `jest.runOnlyPendingTimers()` or `jest.advanceTimersByTime(n)` instead of `runAllTicks()` to bound execution.","Fix the application code to have a termination condition for the nextTick chain.","Increase `maxLoops` via the FakeTimers constructor option if the loop is legitimately large but finite."],"exampleFix":"// before\njest.useFakeTimers({ legacyFakeTimers: true });\n// app code: function poll() { process.nextTick(poll); }\npoll();\njest.runAllTicks(); // throws\n\n// after: use modern timers + bounded advance\njest.useFakeTimers();\npoll();\njest.advanceTimersByTime(1000); // bounded","handlingStrategy":"validation","validationCode":"// Detect recursive nextTick before running all ticks\nfunction isRecursiveNextTick(global) {\n  // heuristic: check if tick queue grows after running one tick\n  const origNextTick = global.process.nextTick;\n  let tickCount = 0;\n  global.process.nextTick = function(cb) { tickCount++; origNextTick(cb); };\n  return tickCount;\n}","typeGuard":null,"tryCatchPattern":"// Wrap timer operations to catch infinite-loop bailouts\ntry {\n  jest.runAllTicks();\n} catch (e) {\n  if (e.message.includes('infinite recursion')) {\n    console.warn('Recursive nextTick detected — use runOnlyPendingTimers or modern timers');\n  }\n  throw e;\n}","preventionTips":["Prefer modern fake timers over legacy for recursive scheduling scenarios.","Use runOnlyPendingTimers instead of runAllTicks for potentially recursive code.","Ensure application code has termination conditions for nextTick chains.","Increase maxLoops only as a last resort after confirming the loop is finite."],"tags":["jest-fake-timers","legacy-fake-timers","infinite-loop","nexttick"],"backgroundTag":null,"analyzedSha":"8e6d128e4a278059ecddecaa97400b04c8ae5fd9","analyzedAt":"2026-08-10T18:11:27.960Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}