Automattic/mongoose · error · MongooseError
Model.findOne() no longer accepts a callback
Error message
Model.findOne() no longer accepts a callback
What it means
Model.findOne(conditions, projection, options) returns a Query resolving to the first match or null. Callbacks were removed in Mongoose 7; a function in any of the first three positional slots throws immediately inside findOne itself.
Source
Thrown at lib/model.js:2204
* // Model.findOne() no longer accepts a callback
*
* // Select only the adventures name and length
* await Adventure.findOne({ country: 'Croatia' }, 'name length').exec();
*
* @param {object} [conditions]
* @param {object|string|string[]} [projection] optional fields to return, see [`Query.prototype.select()`](https://mongoosejs.com/docs/api/query.html#Query.prototype.select())
* @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}
* @see field selection https://mongoosejs.com/docs/api/query.html#Query.prototype.select()
* @see lean queries https://mongoosejs.com/docs/tutorials/lean.html
* @api public
*/
Model.findOne = function findOne(conditions, projection, options) {
_checkContext(this, 'findOne');
if (typeof arguments[0] === 'function' || typeof arguments[1] === 'function' || typeof arguments[2] === 'function') {
throw new MongooseError('Model.findOne() no longer accepts a callback');
}
const mq = new this.Query({}, {}, this, this.$__collection);
mq.select(projection);
mq.setOptions(options);
return mq.findOne(conditions);
};
/**
* Estimates the number of documents in the MongoDB collection. Faster than
* using `countDocuments()` for large collections because
* `estimatedDocumentCount()` uses collection metadata rather than scanning
* the entire collection.
*
* #### Example:
*
* const numAdventures = await Adventure.estimatedDocumentCount();View on GitHub (pinned to 49cdab0136)
Solutions
- Await it: `const user = await User.findOne({ email });`
- Use `.orFail()` when a missing document should reject instead of resolving null
- Sweep `findOne\(` call sites for function arguments during migration
Example fix
// before
User.findOne({ email }, (err, user) => { if (err) throw err; ... });
// after
const user = await User.findOne({ email }); Defensive patterns
Strategy: validation
Validate before calling
const isFn = (a) => typeof a === 'function';
if ([conditions, projection, options].some(isFn)) {
throw new TypeError('findOne() is promise-only');
}
const user = await User.findOne(conditions, projection, options); Prevention
- Auth-style lookups should use .orFail() or explicit null checks after migration
- Cover findOne call sites in the same mechanical migration commit as find()
When it happens
Trigger: `User.findOne({ email }, (err, user) => ...)`; login/lookup handlers written callback-style; passing a callback in place of `options`.
Common situations: Authentication flows (`findOne({ email })`) ported from old tutorials; the upgrade from mongoose 6 to 7+.
Related errors
- Model.find() no longer accepts a callback
- Model.findById() no longer accepts a callback
- Model.findOneAndUpdate() no longer accepts a callback
- Model.init() no longer accepts a callback
- Model.createCollection() no longer accepts a callback
AI-assisted analysis of Automattic/mongoose@49cdab0136 (2026-08-21).
Data as JSON: /api/errors/d65b556518f97639.
Report an issue: GitHub.