angular/angular.js · error · Error

No deferred tasks to be flushed

Error message

No deferred tasks to be flushed

What it means

ngMock replaces the browser with a mock that queues every deferred task (mainly $timeout callbacks) on $browser.defer. defer.flush([delay]) advances the mock clock and runs those tasks; $timeout.flush() delegates to it. When you call flush() with no delay argument and the deferred queue is empty, there is nothing to compute a 'next time' from, which almost always means the test's assumptions are wrong, so it throws immediately.

Source

Thrown at src/ngMock/angular-mocks.js:139

   * @description
   * Flushes all pending requests and executes the defer callbacks.
   *
   * See {@link ngMock.$flushPendingsTasks} for more info.
   *
   * @param {number=} number of milliseconds to flush. See {@link #defer.now}
   */
  self.defer.flush = function(delay) {
    var nextTime;

    if (angular.isDefined(delay)) {
      // A delay was passed so compute the next time
      nextTime = self.defer.now + delay;
    } else if (self.deferredFns.length) {
      // No delay was passed so set the next time so that it clears the deferred queue
      nextTime = self.deferredFns[self.deferredFns.length - 1].time;
    } else {
      // No delay passed, but there are no deferred tasks so flush - indicates an error!
      throw new Error('No deferred tasks to be flushed');
    }

    while (self.deferredFns.length && self.deferredFns[0].time <= nextTime) {
      // Increment the time and call the next deferred function
      self.defer.now = self.deferredFns[0].time;
      var task = self.deferredFns.shift();
      taskTracker.completeTask(task.fn, task.type);
    }

    // Ensure that the current time is correct
    self.defer.now = nextTime;
  };

  /**
   * @name $browser#defer.getPendingTasks
   *
   * @description
   * Returns the currently pending tasks that need to be flushed.

View on GitHub (pinned to d8f77817eb)

Solutions

  1. Make sure the code that schedules the $timeout runs before $timeout.flush() (call the service/controller method first, then flush).
  2. If the timeout may legitimately have been cancelled or already run, guard the flush: only flush when $browser.defer.getPendingTasks().length > 0.
  3. If you only want to advance the clock regardless of pending work, pass an explicit delay: $timeout.flush(500) does not throw on an empty queue.
  4. In afterEach, use $verifyNoPendingTasks('$timeout') to assert state explicitly instead of blindly flushing.

Example fix

// before
it('polls after delay', inject(function($timeout, poller) {
  $timeout.flush(); // throws: nothing scheduled yet
  poller.start();
}));

// after
it('polls after delay', inject(function($timeout, poller) {
  poller.start();            // schedules the $timeout
  $timeout.flush();          // now has something to flush
}));
Defensive patterns

Strategy: validation

Validate before calling

// Inject the mock browser and flush only when work is pending
function flushTimeouts($browser) {
  if ($browser.defer.getPendingTasks().length > 0) {
    $browser.defer.flush();
  }
}
// usage: inject(function($browser){ flushTimeouts($browser); })

Try / catch

// Wrap flush in a helper that adds context on the 'nothing to flush' case
try {
  $timeout.flush();
} catch (e) {
  if (/No deferred tasks to be flushed/.test(e.message)) {
    throw new Error('Test bug: no $timeout pending when flush() was called');
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling $timeout.flush() or $browser.defer.flush() before the code under test has scheduled a $timeout; calling flush a second time after an earlier flush already drained the queue; calling flush after $timeout.cancel() removed the only pending task; calling $timeout.flush() when the async work was actually done with bare $q promises (those are flushed by $digest, not by defer.flush).

Common situations: Test calls $timeout.flush() before invoking the service method that starts the timer; a debounce/poll service cancels its timer on $destroy so nothing is pending; two flush() calls written for one expected timeout; passing flush(delay) is tolerant of an empty queue (nextTime = now + delay, loop no-ops), so intermittent tests that sometimes have zero timers only fail when they call flush() without an argument.

Related errors


AI-assisted analysis of angular/angular.js@d8f77817eb (2026-08-21). Data as JSON: /api/errors/f5efafd5e3a0de62. Report an issue: GitHub.