Automattic/mongoose · error · MongooseError
Query.prototype.deleteMany() no longer accepts a callback
Error message
Query.prototype.deleteMany() no longer accepts a callback
What it means
Query.prototype.deleteMany(filter, options) throws when filter, options, or a third positional argument is a function, mirroring the Mongoose 7 callback removal across all query helpers. The error is thrown while building the query, so bulk-delete cron jobs and scripts from the callback era fail on their first call after upgrading.
Source
Thrown at lib/query.js:3323
*
* #### Example:
*
* const res = await Character.deleteMany({ name: /Stark/, age: { $gte: 18 } });
* // `0` if no docs matched the filter, number of docs deleted otherwise
* res.deletedCount;
*
* @param {object|Query} [filter] mongodb selector
* @param {object} [options] optional see [`Query.prototype.setOptions()`](https://mongoosejs.com/docs/api/query.html#Query.prototype.setOptions())
* @param {boolean} [options.requireFilter=false] If true, throws an error if the filter is empty (`{}`)
* @return {Query} this
* @see DeleteResult https://mongodb.github.io/node-mongodb-native/7.0/interfaces/DeleteResult.html
* @see deleteMany https://mongodb.github.io/node-mongodb-native/7.0/classes/Collection.html#deleteMany
* @api public
*/
Query.prototype.deleteMany = function(filter, options) {
if (typeof filter === 'function' || typeof options === 'function' || typeof arguments[2] === 'function') {
throw new MongooseError('Query.prototype.deleteMany() no longer accepts a callback');
}
this.setOptions(options);
this.op = 'deleteMany';
if (canMerge(filter)) {
this.merge(filter);
prepareDiscriminatorCriteria(this);
} else if (filter != null) {
this.error(new ObjectParameterError(filter, 'filter', 'deleteMany'));
}
return this;
};
/**
* Execute a `deleteMany()` query
*View on GitHub (pinned to 49cdab0136)
Solutions
- Use await: const result = await Model.deleteMany({ stale: true })
- Read result.deletedCount for the number removed
- Sweep for deleteMany( calls with callbacks during the migration
Example fix
// before
Model.deleteMany({ stale: true }, (err, res) => {
if (err) return next(err);
res.json({ removed: res.deletedCount });
});
// after
try {
const result = await Model.deleteMany({ stale: true });
res.json({ removed: result.deletedCount });
} catch (err) {
next(err);
} Defensive patterns
Strategy: validation
Validate before calling
function deleteManySafe(model, filter, options) {
if ([filter, options].some(v => typeof v === 'function')) {
throw new Error('deleteMany() is promise-only in Mongoose 7+');
}
return model.deleteMany(filter, options);
} Type guard
const isLegacyCallback = (v) => typeof v === 'function';
Try / catch
try {
const result = await Model.deleteMany({ stale: true });
} catch (err) {
if (err?.message?.includes('no longer accepts a callback')) {
// fix the deleteMany call site that still passes a callback
}
throw err;
} Prevention
- Rewrite cron/cleanup scripts to awaited deleteMany calls before upgrading
- Prefer explicit filters; Mongoose also warns on empty filters for deletes
- Smoke-test scheduled jobs after a mongoose major upgrade
When it happens
Trigger: Model.deleteMany({ stale: true }, (err) => {...}); .deleteMany(cb); a function in the options slot.
Common situations: Scheduled cleanup jobs and TTL sweepers written for Mongoose 6; upgrades to mongoose 7/8; batch-admin endpoints.
Related errors
- Query.prototype.deleteOne() no longer accepts a callback
- Query.prototype.findOneAndDelete() no longer accepts a callb
- Model.findOneAndDelete() no longer accepts a callback
- Model.findByIdAndDelete() no longer accepts a callback
- Query.prototype.find() no longer accepts a callback
AI-assisted analysis of Automattic/mongoose@49cdab0136 (2026-08-21).
Data as JSON: /api/errors/6f467b7328e6dcc7.
Report an issue: GitHub.