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
- Use const doc = await cursor.next() and loop until it resolves null
- Rewrite as: let doc; while ((doc = await cursor.next()) !== null) {...}
- 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
- Use while ((doc = await cursor.next()) !== null) for manual iteration
- Prefer eachAsync() so exhaustion is handled for you
- Delete unused callback branches during migrations instead of leaving dead paths
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
- QueryCursor.prototype.close() no longer accepts a callback
- QueryCursor.prototype.eachAsync() no longer accepts a callba
- AggregationCursor.prototype.close() no longer accepts a call
- AggregationCursor.prototype.next() no longer accepts a callb
- AggregationCursor.prototype.eachAsync() no longer accepts a
AI-assisted analysis of Automattic/mongoose@49cdab0136 (2026-08-21).
Data as JSON: /api/errors/f31fe259daf30af8.
Report an issue: GitHub.