facebook/react · error · Error

Cannot reset while already flushing work.

Error message

Cannot reset while already flushing work.

What it means

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.

Source

Thrown at packages/scheduler/src/forks/SchedulerMock.js:490

  ) {
    // We yielded at least as many values as expected. Stop flushing.
    didStop = true;
    return true;
  }
  return false;
}

function getCurrentTime(): number {
  return currentMockTime;
}

function forceFrameRate() {
  // No-op
}

function reset() {
  if (isFlushing) {
    throw new Error('Cannot reset while already flushing work.');
  }
  currentMockTime = 0;
  scheduledCallback = null;
  scheduledTimeout = null;
  timeoutTime = -1;
  yieldedValues = null;
  expectedNumberOfYields = -1;
  didStop = false;
  isFlushing = false;
  needsPaint = false;
}

// Should only be used via an assertion helper that inspects the yielded values.
function unstable_flushNumberOfYields(count: number): void {
  if (isFlushing) {
    throw new Error('Already flushing work.');
  }
  if (scheduledCallback !== null) {

View on GitHub (pinned to eafeac097b)

Solutions

  1. 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.
  2. To stop work from inside a task, return false (signal no more work) instead of resetting the mock.
  3. Wrap custom flush wrappers in try/finally so the flush always fully unwinds before any reset runs.
  4. If you share harness code, track your own flushing flag around every flush call and make reset a no-op while it is set.

Example fix

// before
Scheduler.scheduleCallback(NormalPriority, () => {
  Scheduler.unstable_reset(); // throws: a flush is running this callback
});
Scheduler.unstable_flushAll();

// after
Scheduler.scheduleCallback(NormalPriority, () => {
  Scheduler.log('work');
});
Scheduler.unstable_flushAll();
Scheduler.unstable_reset(); // safe: no flush on the stack
Defensive patterns

Strategy: validation

Validate before calling

// The mock exposes no isFlushing getter — track flush nesting yourself.
let flushing = false;
function flush<T>(run: () => T): T {
  if (flushing) throw new Error('nested flush');
  flushing = true;
  try {
    return run();
  } finally {
    flushing = false;
  }
}
function safeReset(Scheduler: any) {
  if (!flushing) Scheduler.unstable_reset(); // skip instead of throwing
}

Try / catch

try {
  Scheduler.unstable_reset();
} catch (e) {
  if (e instanceof Error && e.message.includes('Cannot reset while already flushing')) {
    // defer the reset until the active flush helper returns
  } else {
    throw e;
  }
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of facebook/react@eafeac097b (2026-08-21). Data as JSON: /api/errors/73c898bea7aaf688. Report an issue: GitHub.