Automattic/mongoose · error · MongooseError
Query.prototype.findOneAndUpdate() no longer accepts a callb
Error message
Query.prototype.findOneAndUpdate() no longer accepts a callback
What it means
Query.prototype.findOneAndUpdate(filter, update, options) throws if any of the three declared parameters or a fourth positional argument is a function. Note the argument-shifting logic: with fewer arguments, the method reinterprets positions (one argument is treated as the update with an empty filter), so a lone callback both trips this guard immediately and would otherwise be misparsed as an update document.
Source
Thrown at lib/query.js:3489
* @param {'before'|'after'} [options.returnDocument='before'] Has two possible values, `'before'` and `'after'`. By default, it will return the document before the update was applied.
* @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.
* @param {boolean} [options.overwriteDiscriminatorKey=false] Mongoose removes discriminator key updates from `update` by default, set `overwriteDiscriminatorKey` to `true` to allow updating the discriminator key
* @param {boolean} [options.overwriteImmutable=false] Mongoose removes updated immutable properties from `update` by default (excluding $setOnInsert). Set `overwriteImmutable` to `true` to allow updating immutable properties using other update operators.
* @param {boolean} [options.requireFilter=false] If true, throws an error if the filter is empty (`{}`)
* @see Tutorial https://mongoosejs.com/docs/tutorials/findoneandupdate.html
* @see findAndModify command https://www.mongodb.com/docs/manual/reference/command/findAndModify/
* @see ModifyResult https://mongodb.github.io/node-mongodb-native/7.0/interfaces/ModifyResult.html
* @see findOneAndUpdate https://mongodb.github.io/node-mongodb-native/7.0/classes/Collection.html#findOneAndUpdate
* @return {Query} this
* @api public
*/
Query.prototype.findOneAndUpdate = function(filter, update, options) {
if (typeof filter === 'function' ||
typeof update === 'function' ||
typeof options === 'function' ||
typeof arguments[3] === 'function') {
throw new MongooseError('Query.prototype.findOneAndUpdate() no longer accepts a callback');
}
this.op = 'findOneAndUpdate';
this._validate();
switch (arguments.length) {
case 2:
options = undefined;
break;
case 1:
update = filter;
filter = options = undefined;
break;
}
if (canMerge(filter)) {
this.merge(filter);
} else if (filter != null) {View on GitHub (pinned to 49cdab0136)
Solutions
- Use await: const doc = await Model.findOneAndUpdate(filter, update, { returnDocument: 'after' })
- Handle the not-found case: the promise resolves null when nothing matched
- Remove every trailing callback from findOneAndUpdate call sites; check the migration guide for option renames (new -> returnDocument)
Example fix
// before
Model.findOneAndUpdate({ _id }, { $set: { name: 'x' } }, { new: true }, (err, doc) => {
if (err) return next(err);
res.json(doc);
});
// after
try {
const doc = await Model.findOneAndUpdate(
{ _id },
{ $set: { name: 'x' } },
{ returnDocument: 'after' }
);
res.json(doc);
} catch (err) {
next(err);
} Defensive patterns
Strategy: validation
Validate before calling
function findOneAndUpdateSafe(model, filter, update, options) {
if ([filter, update, options].some(v => typeof v === 'function')) {
throw new Error('findOneAndUpdate() is promise-only in Mongoose 7+');
}
return model.findOneAndUpdate(filter, update, options);
} Type guard
const isLegacyCallback = (v) => typeof v === 'function';
Try / catch
try {
const doc = await Model.findOneAndUpdate(filter, update, { returnDocument: 'after' });
} catch (err) {
if (err?.message?.includes('no longer accepts a callback')) {
// fix the findOneAndUpdate call site that still passes a callback
}
throw err;
} Prevention
- This is the most commonly copied legacy snippet — audit it first in upgrades
- Replace the old { new: true } + callback combo with options + await
- Handle the null result for no match instead of relying on callback err
When it happens
Trigger: Model.findOneAndUpdate({ _id }, { $set: { name: 'x' } }, { returnDocument: 'after' }, cb); .findOneAndUpdate(filter, cb) with the callback in the update slot; Mongoose 5/6 tutorial snippets combining options and callback.
Common situations: Upgrading apps that used Model.findOneAndUpdate(..., { new: true }, cb); the historically most-copied Mongoose snippet; partial migrations leaving one call site behind.
Related errors
- Query.prototype.find() no longer accepts a callback
- Query.prototype.findOne() no longer accepts a callback
- Query.prototype.estimatedDocumentCount() no longer accepts a
- Query.prototype.countDocuments() no longer accepts a callbac
- Query.prototype.distinct() no longer accepts a callback
AI-assisted analysis of Automattic/mongoose@49cdab0136 (2026-08-21).
Data as JSON: /api/errors/7aa32a07c4f1f119.
Report an issue: GitHub.