{"record":{"id":"73c898bea7aaf688","repo":"facebook/react","slug":"cannot-reset-while-already-flushing-work","errorCode":null,"errorMessage":"Cannot reset while already flushing work.","messagePattern":"Cannot reset while already flushing work\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/scheduler/src/forks/SchedulerMock.js","lineNumber":490,"sourceCode":"  ) {\n    // We yielded at least as many values as expected. Stop flushing.\n    didStop = true;\n    return true;\n  }\n  return false;\n}\n\nfunction getCurrentTime(): number {\n  return currentMockTime;\n}\n\nfunction forceFrameRate() {\n  // No-op\n}\n\nfunction reset() {\n  if (isFlushing) {\n    throw new Error('Cannot reset while already flushing work.');\n  }\n  currentMockTime = 0;\n  scheduledCallback = null;\n  scheduledTimeout = null;\n  timeoutTime = -1;\n  yieldedValues = null;\n  expectedNumberOfYields = -1;\n  didStop = false;\n  isFlushing = false;\n  needsPaint = false;\n}\n\n// Should only be used via an assertion helper that inspects the yielded values.\nfunction unstable_flushNumberOfYields(count: number): void {\n  if (isFlushing) {\n    throw new Error('Already flushing work.');\n  }\n  if (scheduledCallback !== null) {","sourceCodeStart":472,"sourceCodeEnd":508,"githubUrl":"https://github.com/facebook/react/blob/eafeac097ba51e1eab809c07102126bd5f8e5425/packages/scheduler/src/forks/SchedulerMock.js#L472-L508","documentation":"The Scheduler package ships a test-only mock (imported in Jest via jest.mock('scheduler', () => jest.requireActual('scheduler/unstable_mock'))) whose flush helpers run queued callbacks in a synchronous do-while loop while a module-level isFlushing flag is set. unstable_reset() wipes every piece of mock state (currentMockTime, scheduledCallback, yieldedValues, expectedNumberOfYields), and it throws 'Cannot reset while already flushing work.' when called mid-flush, because nulling that state underneath the running loop would corrupt the flush invariants. It is a test-harness invariant error, never a production scheduler error.","triggerScenarios":"Calling Scheduler.unstable_reset() from inside a task callback that unstable_flushAll(), unstable_flushNumberOfYields(), unstable_flushUntilNextPaint(), unstable_flushExpired(), or unstable_flushAllWithoutAsserting() is currently executing; or a nested helper (afterEach, custom act() wrapper) invoking reset() on the same stack as an active flush that has not unwound.","commonSituations":"Refactoring React component tests that drive the scheduler mock manually; a task callback tries to 'clean up between scenarios' by resetting the mock; shared test-utils that wrap flushes without try/finally; state leaking between tests when a prior test aborted mid-flush.","solutions":["Call Scheduler.unstable_reset() only between flushes — e.g. at the top of beforeEach/afterEach after the previous flush helper has fully returned — never from inside a scheduled callback.","To stop work from inside a task, return false (signal no more work) instead of resetting the mock.","Wrap custom flush wrappers in try/finally so the flush always fully unwinds before any reset runs.","If you share harness code, track your own flushing flag around every flush call and make reset a no-op while it is set."],"exampleFix":"// before\nScheduler.scheduleCallback(NormalPriority, () => {\n  Scheduler.unstable_reset(); // throws: a flush is running this callback\n});\nScheduler.unstable_flushAll();\n\n// after\nScheduler.scheduleCallback(NormalPriority, () => {\n  Scheduler.log('work');\n});\nScheduler.unstable_flushAll();\nScheduler.unstable_reset(); // safe: no flush on the stack","handlingStrategy":"validation","validationCode":"// The mock exposes no isFlushing getter — track flush nesting yourself.\nlet flushing = false;\nfunction flush<T>(run: () => T): T {\n  if (flushing) throw new Error('nested flush');\n  flushing = true;\n  try {\n    return run();\n  } finally {\n    flushing = false;\n  }\n}\nfunction safeReset(Scheduler: any) {\n  if (!flushing) Scheduler.unstable_reset(); // skip instead of throwing\n}","typeGuard":null,"tryCatchPattern":"try {\n  Scheduler.unstable_reset();\n} catch (e) {\n  if (e instanceof Error && e.message.includes('Cannot reset while already flushing')) {\n    // defer the reset until the active flush helper returns\n  } else {\n    throw e;\n  }\n}","preventionTips":["Never call unstable_reset() from inside a scheduled callback or an assertion helper that runs during a flush.","Keep reset calls at the top level of tests (beforeEach/afterEach), after flush helpers have returned.","Treat the scheduler mock as single-entrant: at most one flush helper on the stack at any time."],"tags":["scheduler","testing","mock","reentrancy"],"backgroundTag":"reset-during-active-operation","analyzedSha":"eafeac097ba51e1eab809c07102126bd5f8e5425","analyzedAt":"2026-08-21T22:01:08.818Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}