Automattic/mongoose · error · MongooseError
Model.findById() no longer accepts a callback
Error message
Model.findById() no longer accepts a callback
What it means
Model.findById(id, projection, options) is sugar for findOne({ _id: id }); Mongoose 7 removed callbacks, so passing a function in any of the first three argument slots throws before the findOne delegation happens. Note a callback passed as `id` itself also triggers this.
Source
Thrown at lib/model.js:2166
* await Adventure.findById(id).exec();
*
* // select only the adventures name and length
* await Adventure.findById(id, 'name length').exec();
*
* @param {any} id value of `_id` to query by
* @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())
* @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
* @see findById in Mongoose https://masteringjs.io/tutorials/mongoose/find-by-id
* @api public
*/
Model.findById = function findById(id, projection, options) {
_checkContext(this, 'findById');
if (typeof arguments[0] === 'function' || typeof arguments[1] === 'function' || typeof arguments[2] === 'function') {
throw new MongooseError('Model.findById() no longer accepts a callback');
}
return this.findOne({ _id: id }, projection, options);
};
/**
* Finds one document.
*
* The `conditions` are cast to their respective SchemaTypes before the command is sent.
*
* *Note:* `conditions` is optional, and if `conditions` is null or undefined,
* mongoose will send an empty `findOne` command to MongoDB, which will return
* an arbitrary document. If you're querying by `_id`, use `findById()` instead.
*
* #### Example:
*
* // Find one adventure whose `country` is 'Croatia', otherwise `null`
* await Adventure.findOne({ country: 'Croatia' }).exec();View on GitHub (pinned to 49cdab0136)
Solutions
- Await it: `const user = await User.findById(id);`
- Handle not-found explicitly (result is null when missing)
- Migrate route handlers in bulk — search `findById\(` with a following function argument
Example fix
// before
User.findById(req.params.id, (err, user) => {
if (err) return next(err);
res.json(user);
});
// after
const user = await User.findById(req.params.id);
res.json(user); Defensive patterns
Strategy: validation
Validate before calling
const isFn = (a) => typeof a === 'function';
if ([id, projection, options].some(isFn)) {
throw new TypeError('findById() is promise-only');
}
const user = await User.findById(id, projection, options); Prevention
- Handle the null result explicitly (404 path) — the old callback's err argument never covered missing docs
- TypeScript types remove the callback overloads; adopt them at the API boundary
When it happens
Trigger: `User.findById(id, (err, user) => ...)`; `User.findById(cb)`; older route handlers from Express + Mongoose 5/6 tutorials.
Common situations: Classic `GET /users/:id` handlers copied from pre-2023 tutorials; the most-hit call site family after find() during major upgrades.
Related errors
- Model.find() no longer accepts a callback
- Model.findOne() 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/d10e7b3805860aea.
Report an issue: GitHub.