Automattic/mongoose · error · MongooseError
Model.countDocuments() no longer accepts a callback
Error message
Model.countDocuments() no longer accepts a callback
What it means
Model.countDocuments(conditions, options) runs a count with schema-based casting of the filter and returns a Query resolving to a number. Mongoose 7 removed callbacks: a function in any of the first three argument slots throws synchronously.
Source
Thrown at lib/model.js:2267
*
* The `countDocuments()` function is similar to `count()`, but there are a
* [few operators that `countDocuments()` does not support](https://mongodb.github.io/node-mongodb-native/7.0/classes/Collection.html#countDocuments).
* Below are the operators that `count()` supports but `countDocuments()` does not,
* and the suggested replacement:
*
* - `$where`: [`$expr`](https://www.mongodb.com/docs/manual/reference/operator/query/expr/)
* - `$near`: [`$geoWithin`](https://www.mongodb.com/docs/manual/reference/operator/query/geoWithin/) with [`$center`](https://www.mongodb.com/docs/manual/reference/operator/query/center/#op._S_center)
* - `$nearSphere`: [`$geoWithin`](https://www.mongodb.com/docs/manual/reference/operator/query/geoWithin/) with [`$centerSphere`](https://www.mongodb.com/docs/manual/reference/operator/query/centerSphere/#op._S_centerSphere)
*
* @param {object} filter
* @return {Query}
* @api public
*/
Model.countDocuments = function countDocuments(conditions, options) {
_checkContext(this, 'countDocuments');
if (typeof arguments[0] === 'function' || typeof arguments[1] === 'function' || typeof arguments[2] === 'function') {
throw new MongooseError('Model.countDocuments() no longer accepts a callback');
}
const mq = new this.Query({}, {}, this, this.$__collection);
if (options != null) {
mq.setOptions(options);
}
return mq.countDocuments(conditions);
};
/**
* Creates a Query for a `distinct` operation.
*
* #### Example:
*
* const query = Link.distinct('url');
* query.exec();View on GitHub (pinned to 49cdab0136)
Solutions
- Await it: `const n = await User.countDocuments({ active: true });`
- For whole-collection estimates use `estimatedDocumentCount()` (also promise-only)
- Include `countDocuments\(` in the migration grep list
Example fix
// before
User.countDocuments({ active: true }, (err, count) => { ... });
// after
const count = await User.countDocuments({ active: true }); Defensive patterns
Strategy: validation
Validate before calling
const isFn = (a) => typeof a === 'function';
if ([conditions, options].some(isFn)) {
throw new TypeError('countDocuments() is promise-only');
}
const count = await User.countDocuments(conditions); Prevention
- Remember unsupported filter operators ($where/$near) reject at runtime now — validate filters server-side
- Use estimatedDocumentCount() for unfiltered totals instead of porting old callback counts
When it happens
Trigger: `User.countDocuments({ active: true }, cb)`; `User.countDocuments(cb)` for total counts; pagination helpers from the callback era.
Common situations: Pagination/ dashboard count queries written pre-Mongoose-7; upgrade sweeps that missed count calls because they are less frequent than find/findOne.
Related errors
- Model.init() no longer accepts a callback
- Model.createCollection() no longer accepts a callback
- Model.syncIndexes() no longer accepts a callback
- Model.cleanIndexes() no longer accepts a callback
- Model.listIndexes() no longer accepts a callback
AI-assisted analysis of Automattic/mongoose@49cdab0136 (2026-08-21).
Data as JSON: /api/errors/37cd8517e928339f.
Report an issue: GitHub.