Automattic/mongoose · error · MongooseError

AggregationCursor.prototype.next() no longer accepts a callb

Error message

AggregationCursor.prototype.next() no longer accepts a callback

What it means

AggregationCursor#next() became promise-based when Mongoose 7 dropped callbacks. Calling it with a function as the first argument throws immediately; next() now resolves with the next document or null when the cursor is exhausted.

Source

Thrown at lib/cursor/aggregationCursor.js:293

    })
    .catch(error => {
      callback(error);
    });
  return this;
};

/**
 * Get the next document from this cursor. Will return `null` when there are
 * no documents left.
 *
 * @return {Promise}
 * @api public
 * @method next
 */

AggregationCursor.prototype.next = async function next() {
  if (typeof arguments[0] === 'function') {
    throw new MongooseError('AggregationCursor.prototype.next() no longer accepts a callback');
  }
  const _this = this;
  const model = this.agg._model;
  return cursorNextChannel.trace(function maybeTracedAggCursorNext() {
    return new Promise((resolve, reject) => {
      _next(_this, (err, res) => {
        if (err != null) {
          return reject(err);
        }
        resolve(res);
      });
    });
  }, () => ({
    operation: 'aggregate',
    collection: model?.collection?.name,
    database: model?.db?.name,
    serverAddress: model?.db?.host,
    serverPort: model?.db?.port,

View on GitHub (pinned to 49cdab0136)

Solutions

  1. Use const doc = await cursor.next() and loop until it resolves null
  2. Rewrite callback pagination as: while ((doc = await cursor.next()) !== null) {...}
  3. For bulk processing prefer eachAsync(fn, { parallel }) over manual next() loops

Example fix

// before
aggCursor.next(function (err, doc) {
  if (err) throw err;
  if (!doc) return done();
  handle(doc);
});

// after
let doc;
while ((doc = await aggCursor.next()) !== null) {
  handle(doc);
}
Defensive patterns

Strategy: validation

Validate before calling

function nextDoc(cursor, ...args) {
  if (args.some(a => typeof a === 'function')) {
    throw new TypeError('next() takes no callback; await the returned promise');
  }
  return cursor.next();
}

Prevention

When it happens

Trigger: aggCursor.next((err, doc) => {...}) — any function passed as the first argument to next().

Common situations: Legacy iteration loops left unmigrated during a Mongoose 6 to 7+ upgrade; old tutorials and Stack Overflow snippets using callback-style cursors.

Related errors


AI-assisted analysis of Automattic/mongoose@49cdab0136 (2026-08-21). Data as JSON: /api/errors/380c30dbafccd584. Report an issue: GitHub.