{"record":{"id":"f31fe259daf30af8","repo":"Automattic/mongoose","slug":"querycursor-prototype-next-no-longer-accepts-a-c","errorCode":null,"errorMessage":"QueryCursor.prototype.next() no longer accepts a callback","messagePattern":"QueryCursor\\.prototype\\.next\\(\\) no longer accepts a callback","errorType":"exception","errorClass":"MongooseError","httpStatus":null,"severity":"error","filePath":"lib/cursor/queryCursor.js","lineNumber":309,"sourceCode":"QueryCursor.prototype.rewind = function() {\n  _waitForCursor(this, () => {\n    this.cursor.rewind();\n  });\n  return this;\n};\n\n/**\n * Get the next document from this cursor. Will return `null` when there are\n * no documents left.\n *\n * @return {Promise}\n * @api public\n * @method next\n */\n\nQueryCursor.prototype.next = async function next() {\n  if (typeof arguments[0] === 'function') {\n    throw new MongooseError('QueryCursor.prototype.next() no longer accepts a callback');\n  }\n  if (this._closed) {\n    throw new MongooseError('Cannot call `next()` on a closed cursor');\n  }\n  const _this = this;\n  return cursorNextChannel.trace(function maybeTracedQueryCursorNext() {\n    return new Promise((resolve, reject) => {\n      _next(_this, function(error, doc) {\n        if (error) {\n          return reject(error);\n        }\n        resolve(doc);\n      });\n    });\n  }, () => ({\n    operation: _this.query.op || 'find',\n    collection: _this.query.mongooseCollection.name,\n    database: _this.model.db?.name,","sourceCodeStart":291,"sourceCodeEnd":327,"githubUrl":"https://github.com/Automattic/mongoose/blob/49cdab01366679723b487ecb754b38570f783289/lib/cursor/queryCursor.js#L291-L327","documentation":"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.","triggerScenarios":"queryCursor.next((err, doc) => {...}) — any function passed as the first argument to next() on a cursor from Model.find().cursor().","commonSituations":"Legacy manual iteration loops surviving a Mongoose 6 to 7+ upgrade; batch jobs copied from older examples.","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"],"exampleFix":"// before\nconst cursor = Book.find().cursor();\ncursor.next(function (err, doc) {\n  if (err) throw err;\n  if (!doc) return finish();\n  handle(doc);\n});\n\n// after\nconst cursor = Book.find().cursor();\nlet doc;\nwhile ((doc = await cursor.next()) !== null) {\n  handle(doc);\n}","handlingStrategy":"validation","validationCode":"function nextDoc(cursor, ...args) {\n  if (args.some(a => typeof a === 'function')) {\n    throw new TypeError('next() takes no callback; await the returned promise');\n  }\n  return cursor.next();\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["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"],"tags":["query-cursor","callback","promise","mongoose-7","migration"],"backgroundTag":"callback-api-removed","analyzedSha":"49cdab01366679723b487ecb754b38570f783289","analyzedAt":"2026-08-21T22:54:00.882Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}