Automattic/mongoose · error · MongooseError
Query.prototype.deleteOne() no longer accepts a callback
Error message
Query.prototype.deleteOne() no longer accepts a callback
What it means
Query.prototype.deleteOne(filter, options) throws when filter, options, or a third positional argument is a function — the Mongoose 7 removal of callback execution. The throw is synchronous at query-construction time, so legacy Model.deleteOne({}, cb) never reaches the database.
Source
Thrown at lib/query.js:3247
*
* #### Example:
*
* const res = await Character.deleteOne({ name: 'Eddard Stark' });
* // `1` if MongoDB deleted a doc, `0` if no docs matched the filter `{ name: ... }`
* 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 deleteOne https://mongodb.github.io/node-mongodb-native/7.0/classes/Collection.html#deleteOne
* @api public
*/
Query.prototype.deleteOne = function deleteOne(filter, options) {
if (typeof filter === 'function' || typeof options === 'function' || typeof arguments[2] === 'function') {
throw new MongooseError('Query.prototype.deleteOne() no longer accepts a callback');
}
this.op = 'deleteOne';
this.setOptions(options);
if (canMerge(filter)) {
this.merge(filter);
prepareDiscriminatorCriteria(this);
} else if (filter != null) {
this.error(new ObjectParameterError(filter, 'filter', 'deleteOne'));
}
return this;
};
/**
* Internal thunk for `deleteOne()`
*View on GitHub (pinned to 49cdab0136)
Solutions
- Use await: const result = await Model.deleteOne({ _id }) and read result.deletedCount
- Handle the rejected promise with try/catch or .catch()
- During upgrade, grep for deleteOne( with trailing function arguments
Example fix
// before
Model.deleteOne({ _id }, (err) => {
if (err) return next(err);
res.status(204).end();
});
// after
try {
await Model.deleteOne({ _id });
res.status(204).end();
} catch (err) {
next(err);
} Defensive patterns
Strategy: validation
Validate before calling
function deleteOneSafe(model, filter, options) {
if ([filter, options].some(v => typeof v === 'function')) {
throw new Error('deleteOne() is promise-only in Mongoose 7+');
}
return model.deleteOne(filter, options);
} Type guard
const isLegacyCallback = (v) => typeof v === 'function';
Try / catch
try {
const result = await Model.deleteOne({ _id });
} catch (err) {
if (err?.message?.includes('no longer accepts a callback')) {
// fix the deleteOne call site that still passes a callback
}
throw err;
} Prevention
- Await deleteOne and read result.deletedCount
- Audit admin/cleanup routes for callback signatures when upgrading
- Keep delete helpers in a data-access layer so signatures are checked in one place
When it happens
Trigger: Model.deleteOne({ _id }, (err) => {...}); .deleteOne(cb) with the callback in the filter slot; a function passed in the options slot.
Common situations: Cleanup and admin routes written against Mongoose 6; major-version upgrades; copy-pasted deletion snippets from old tutorials.
Related errors
- Query.prototype.deleteMany() 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/248fb6cd8d4992a8.
Report an issue: GitHub.