Automattic/mongoose · error · MongooseError
Model.deleteMany() no longer accepts a callback
Error message
Model.deleteMany() no longer accepts a callback
What it means
Model.deleteMany(conditions, options) deletes all matching documents via a Query. As with every query static, Mongoose 7 dropped callback support: passing a function in any of the first three positional slots throws before the Query is even built.
Source
Thrown at lib/model.js:2083
* await Character.deleteMany({ name: /Stark/, age: { $gte: 18 } }); // returns {deletedCount: x} where x is the number of documents deleted.
*
* #### Note:
*
* This function triggers `deleteMany` query hooks. Read the
* [middleware docs](https://mongoosejs.com/docs/middleware.html#naming) to learn more.
*
* @param {object} conditions
* @param {object} [options] optional see [`Query.prototype.setOptions()`](https://mongoosejs.com/docs/api/query.html#Query.prototype.setOptions())
* @param {boolean} [options.translateAliases=null] If set to `true`, translates any schema-defined aliases in `filter`, `projection`, `update`, and `distinct`. Throws an error if there are any conflicts where both alias and raw property are defined on the same object.
* @return {Query}
* @api public
*/
Model.deleteMany = function deleteMany(conditions, options) {
_checkContext(this, 'deleteMany');
if (typeof arguments[0] === 'function' || typeof arguments[1] === 'function' || typeof arguments[2] === 'function') {
throw new MongooseError('Model.deleteMany() no longer accepts a callback');
}
const mq = new this.Query({}, {}, this, this.$__collection);
mq.setOptions(options);
return mq.deleteMany(conditions);
};
/**
* Finds documents.
*
* Mongoose casts the `filter` to match the model's schema before the command is sent.
* See our [query casting tutorial](https://mongoosejs.com/docs/tutorials/query_casting.html) for
* more information on how Mongoose casts `filter`.
*
* #### Example:
*
* // find all documentsView on GitHub (pinned to 49cdab0136)
Solutions
- Await it: `const { deletedCount } = await User.deleteMany({ inactive: true });`
- Migrate error handling to try/catch around the await
- Sweep for `\.deleteMany\(` with function arguments repo-wide
Example fix
// before
User.deleteMany({ inactive: true }, (err) => { ... });
// after
const { deletedCount } = await User.deleteMany({ inactive: true }); Defensive patterns
Strategy: validation
Validate before calling
const isFn = (a) => typeof a === 'function';
if ([conditions, options].some(isFn)) throw new TypeError('deleteMany() is promise-only');
const { deletedCount } = await User.deleteMany(conditions, options); Prevention
- Convert bulk-cleanup jobs to await-based loops; old callback counters hide these errors until runtime
- Keep the migration grep list for deletes next to finds so none are missed
When it happens
Trigger: `User.deleteMany({ inactive: true }, cb)`; cleanup jobs from the callback era; `User.deleteMany(cb)` intending delete-all.
Common situations: Post-migration leftovers; bulk-cleanup cron scripts that were never touched during the upgrade.
Related errors
- Model.init() no longer accepts a callback
- Model.createCollection() no longer accepts a callback
- Model.syncIndexes() no longer accepts a callback
- Model.cleanIndexes() no longer accepts a callback
- Model.listIndexes() no longer accepts a callback
AI-assisted analysis of Automattic/mongoose@49cdab0136 (2026-08-21).
Data as JSON: /api/errors/3995df0a45f62206.
Report an issue: GitHub.