Automattic/mongoose · error · MongooseError
Model.insertMany() no longer accepts a callback
Error message
Model.insertMany() no longer accepts a callback
What it means
Model.insertMany() is async and promise-only in Mongoose 7+; the guard throws (as a rejected promise) when options is a function or a third argument is a function, covering both legacy forms insertMany(docs, callback) and insertMany(docs, options, callback).
Source
Thrown at lib/model.js:3032
* @param {object} [options] see the [mongodb driver options](https://mongodb.github.io/node-mongodb-native/7.0/classes/Collection.html#insertMany)
* @param {boolean} [options.ordered=true] if true, will fail fast on the first error encountered. If false, will insert all the documents it can and report errors later. An `insertMany()` with `ordered = false` is called an "unordered" `insertMany()`.
* @param {boolean} [options.rawResult=false] if false, the returned promise resolves to the documents that passed mongoose document validation. If `true`, will return the [raw result from the MongoDB driver](https://mongodb.github.io/node-mongodb-native/7.0/interfaces/InsertManyResult.html) with a `mongoose` property that contains `validationErrors` and `results` if this is an unordered `insertMany`.
* @param {boolean} [options.lean=false] if `true`, skips hydrating the documents. This means Mongoose will **not** cast, validate, or apply defaults to any of the documents passed to `insertMany()`. This option is useful if you need the extra performance, but comes with data integrity risk. Consider using with [`castObject()`](https://mongoosejs.com/docs/api/model.html#Model.castObject()) and [`applyDefaults()`](https://mongoosejs.com/docs/api/model.html#Model.applyDefaults()).
* @param {number} [options.limit=null] this limits the number of documents being processed (validation/casting) by mongoose in parallel, this does **NOT** send the documents in batches to MongoDB. Use this option if you're processing a large number of documents and your app is running out of memory.
* @param {string|object|Array} [options.populate=null] populates the result documents. This option is a no-op if `rawResult` is set.
* @param {boolean} [options.throwOnValidationError=false] If true and `ordered: false`, throw an error if one of the operations failed validation, but all valid operations completed successfully.
* @param {boolean|object} [options.middleware=true] set to `false` to skip all user-defined middleware
* @param {boolean} [options.middleware.pre=true] set to `false` to skip only pre hooks
* @param {boolean} [options.middleware.post=true] set to `false` to skip only post hooks
* @return {Promise} resolving to the raw result from the MongoDB driver if `options.rawResult` was `true`, or the documents that passed validation, otherwise
* @api public
*/
Model.insertMany = async function insertMany(arr, options) {
_checkContext(this, 'insertMany');
if (typeof options === 'function' ||
typeof arguments[2] === 'function') {
throw new MongooseError('Model.insertMany() no longer accepts a callback');
}
const ThisModel = this;
return traceInsertMany(function maybeTracedInsertMany() { return _insertMany.call(ThisModel, arr, options); }, () => ({
operation: 'insertMany',
collection: ThisModel.collection.name,
database: ThisModel.db?.name,
serverAddress: ThisModel.db?.host,
serverPort: ThisModel.db?.port,
args: { docs: arr, options }
}));
};
async function _insertMany(arr, options) {
options = options || {};
const hasInsertManyHooks = this._middleware.hasHooks('insertMany');
const preFilter = hasInsertManyHooks ? buildMiddlewareFilter(options, 'pre') : null;
const postFilter = hasInsertManyHooks ? buildMiddlewareFilter(options, 'post') : null;View on GitHub (pinned to 49cdab0136)
Solutions
- Use await: const docs = await Model.insertMany(docs, { ordered: false }); inside try/catch
- Or .then()/.catch() on the returned promise
- Sweep for callback patterns on all bulk methods (insertMany, bulkWrite, create) during the Mongoose 7 migration
- Pin mongoose@6 temporarily if the rewrite must be deferred
- Use TypeScript so callback overloads fail to compile
Example fix
// before
User.insertMany(users, (err, docs) => { ... });
// after
const docs = await User.insertMany(users); Defensive patterns
Strategy: validation
Validate before calling
function insertManySafe(Model, docs, options) {
if (typeof options === 'function') throw new TypeError('insertMany: use await, not a callback');
if (arguments.length > 3) throw new TypeError('insertMany: too many arguments');
return Model.insertMany(docs, options);
} Try / catch
try {
const saved = await Model.insertMany(docs, { ordered: false });
} catch (err) {
if (/no longer accepts a callback/.test(err.message)) { /* migrate call site to await */ }
else throw err;
} Prevention
- Include insertMany and bulkWrite in the same Mongoose 7 migration pass as create()
- Use TypeScript: promise-only signatures catch callback forms at compile time
- Wrap bulk operations in one promise-based repository method instead of scattering call styles
When it happens
Trigger: Model.insertMany(docs, cb) with the callback in the options slot, or Model.insertMany(docs, { ordered: false }, cb) with the callback as the third argument.
Common situations: Seed scripts and bulk-import jobs written for Mongoose 6; upgrading a codebase where only the insertMany call sites were missed; older libraries wrapping insertMany with callbacks.
Related errors
- Model.bulkWrite() no longer accepts a callback
- Model.findByIdAndUpdate() no longer accepts a callback
- Model.findOneAndDelete() no longer accepts a callback
- Model.findByIdAndDelete() no longer accepts a callback
- Model.findOneAndReplace() no longer accepts a callback
AI-assisted analysis of Automattic/mongoose@49cdab0136 (2026-08-21).
Data as JSON: /api/errors/1e4caabc6322c1df.
Report an issue: GitHub.