{"record":{"id":"499deeaf2b0f62b9","repo":"Automattic/mongoose","slug":"aggregationcursor-prototype-eachasync-no-longer","errorCode":null,"errorMessage":"AggregationCursor.prototype.eachAsync() no longer accepts a callback","messagePattern":"AggregationCursor\\.prototype\\.eachAsync\\(\\) no longer accepts a callback","errorType":"exception","errorClass":"MongooseError","httpStatus":null,"severity":"error","filePath":"lib/cursor/aggregationCursor.js","lineNumber":338,"sourceCode":"\n/**\n * Execute `fn` for every document in the cursor. If `fn` returns a promise,\n * will wait for the promise to resolve before iterating on to the next one.\n * Returns a promise that resolves when done.\n *\n * @param {Function} fn\n * @param {object} [options]\n * @param {number} [options.parallel] the number of promises to execute in parallel. Defaults to 1.\n * @param {number} [options.batchSize=null] if set, Mongoose will call `fn` with an array of at most `batchSize` documents, instead of a single document\n * @param {boolean} [options.continueOnError=false] if true, `eachAsync()` iterates through all docs even if `fn` throws an error. If false, `eachAsync()` throws an error immediately if the given function `fn()` throws an error.\n * @return {Promise}\n * @api public\n * @method eachAsync\n */\n\nAggregationCursor.prototype.eachAsync = function(fn, opts) {\n  if (typeof arguments[2] === 'function') {\n    throw new MongooseError('AggregationCursor.prototype.eachAsync() no longer accepts a callback');\n  }\n  const _this = this;\n  if (typeof opts === 'function') {\n    opts = {};\n  }\n  opts = opts || {};\n\n  return eachAsync(function(cb) { return _next(_this, cb); }, fn, opts);\n};\n\n/**\n * Returns an asyncIterator for use with [`for/await/of` loops](https://thecodebarbarian.com/getting-started-with-async-iterators-in-node-js)\n * You do not need to call this function explicitly, the JavaScript runtime\n * will call it for you.\n *\n * #### Example:\n *\n *     // Async iterator without explicitly calling `cursor()`. Mongoose still","sourceCodeStart":320,"sourceCodeEnd":356,"githubUrl":"https://github.com/Automattic/mongoose/blob/49cdab01366679723b487ecb754b38570f783289/lib/cursor/aggregationCursor.js#L320-L356","documentation":"eachAsync() on an aggregation cursor no longer accepts a completion callback as its third argument in Mongoose 7+. Passing (fn, opts, cb) throws immediately; the method returns a Promise that settles when iteration finishes. Note the guard checks arguments[2] specifically — passing (fn, cb) does not throw, but that callback is silently ignored.","triggerScenarios":"aggCursor.eachAsync(fn, {}, cb) or aggCursor.eachAsync(fn, { batchSize: 10 }, function (err) {...}) — a function in the third argument slot.","commonSituations":"Migrating ETL scripts that used eachAsync with callbacks; combining options like parallel/batchSize with a legacy completion handler.","solutions":["Drop the callback and await: await aggCursor.eachAsync(fn, { parallel: 4 })","Keep error handling in a try/catch around the await","If a callback interface must be preserved outward, wrap: eachAsync(fn, opts).then(() => cb(null), cb)"],"exampleFix":"// before\naggCursor.eachAsync(doc => save(doc), { parallel: 4 }, function (err) {\n  if (err) return done(err);\n  done();\n});\n\n// after\ntry {\n  await aggCursor.eachAsync(doc => save(doc), { parallel: 4 });\n  done();\n} catch (err) {\n  done(err);\n}","handlingStrategy":"validation","validationCode":"function runEachAsync(cursor, fn, opts, cb) {\n  if (typeof cb === 'function') {\n    // legacy 3-arg call: convert to promise, keep behavior explicit\n    return cursor.eachAsync(fn, opts).then(() => cb(null), cb);\n  }\n  return cursor.eachAsync(fn, opts);\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Remember the exact trigger: only the third argument (fn, opts, cb) throws; a (fn, cb) call silently drops the callback","Standardize on await eachAsync(fn, opts) plus try/catch for errors","Cover cursor code paths in migration tests so unmigrated callbacks fail in CI, not production"],"tags":["aggregation-cursor","eachasync","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"}