Automattic/mongoose · error · MongooseError

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

Error message

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

What it means

QueryCursor#next() is promise-based in Mongoose 7+; callbacks were removed across the cursor API. Calling next() with a function as the first argument throws immediately — next() now resolves with the next document or null at exhaustion.

Source

Thrown at lib/cursor/queryCursor.js:309

QueryCursor.prototype.rewind = function() {
  _waitForCursor(this, () => {
    this.cursor.rewind();
  });
  return this;
};

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

QueryCursor.prototype.next = async function next() {
  if (typeof arguments[0] === 'function') {
    throw new MongooseError('QueryCursor.prototype.next() no longer accepts a callback');
  }
  if (this._closed) {
    throw new MongooseError('Cannot call `next()` on a closed cursor');
  }
  const _this = this;
  return cursorNextChannel.trace(function maybeTracedQueryCursorNext() {
    return new Promise((resolve, reject) => {
      _next(_this, function(error, doc) {
        if (error) {
          return reject(error);
        }
        resolve(doc);
      });
    });
  }, () => ({
    operation: _this.query.op || 'find',
    collection: _this.query.mongooseCollection.name,
    database: _this.model.db?.name,

View on GitHub (pinned to 49cdab0136)

Solutions

  1. Use const doc = await cursor.next() and loop until it resolves null
  2. Rewrite as: let doc; while ((doc = await cursor.next()) !== null) {...}
  3. Prefer eachAsync(fn, opts) for structured iteration instead of manual next() loops

Example fix

// before
const cursor = Book.find().cursor();
cursor.next(function (err, doc) {
  if (err) throw err;
  if (!doc) return finish();
  handle(doc);
});

// after
const cursor = Book.find().cursor();
let doc;
while ((doc = await cursor.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: queryCursor.next((err, doc) => {...}) — any function passed as the first argument to next() on a cursor from Model.find().cursor().

Common situations: Legacy manual iteration loops surviving a Mongoose 6 to 7+ upgrade; batch jobs copied from older examples.

Related errors


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