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

  1. Use await: const docs = await Model.insertMany(docs, { ordered: false }); inside try/catch
  2. Or .then()/.catch() on the returned promise
  3. Sweep for callback patterns on all bulk methods (insertMany, bulkWrite, create) during the Mongoose 7 migration
  4. Pin mongoose@6 temporarily if the rewrite must be deferred
  5. 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

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


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