{"record":{"id":"37cd8517e928339f","repo":"Automattic/mongoose","slug":"model-countdocuments-no-longer-accepts-a-callbac","errorCode":null,"errorMessage":"Model.countDocuments() no longer accepts a callback","messagePattern":"Model\\.countDocuments\\(\\) no longer accepts a callback","errorType":"exception","errorClass":"MongooseError","httpStatus":null,"severity":"error","filePath":"lib/model.js","lineNumber":2267,"sourceCode":" *\n * The `countDocuments()` function is similar to `count()`, but there are a\n * [few operators that `countDocuments()` does not support](https://mongodb.github.io/node-mongodb-native/7.0/classes/Collection.html#countDocuments).\n * Below are the operators that `count()` supports but `countDocuments()` does not,\n * and the suggested replacement:\n *\n * - `$where`: [`$expr`](https://www.mongodb.com/docs/manual/reference/operator/query/expr/)\n * - `$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)\n * - `$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)\n *\n * @param {object} filter\n * @return {Query}\n * @api public\n */\n\nModel.countDocuments = function countDocuments(conditions, options) {\n  _checkContext(this, 'countDocuments');\n  if (typeof arguments[0] === 'function' || typeof arguments[1] === 'function' || typeof arguments[2] === 'function') {\n    throw new MongooseError('Model.countDocuments() no longer accepts a callback');\n  }\n\n  const mq = new this.Query({}, {}, this, this.$__collection);\n  if (options != null) {\n    mq.setOptions(options);\n  }\n\n  return mq.countDocuments(conditions);\n};\n\n\n/**\n * Creates a Query for a `distinct` operation.\n *\n * #### Example:\n *\n *     const query = Link.distinct('url');\n *     query.exec();","sourceCodeStart":2249,"sourceCodeEnd":2285,"githubUrl":"https://github.com/Automattic/mongoose/blob/49cdab01366679723b487ecb754b38570f783289/lib/model.js#L2249-L2285","documentation":"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.","triggerScenarios":"`User.countDocuments({ active: true }, cb)`; `User.countDocuments(cb)` for total counts; pagination helpers from the callback era.","commonSituations":"Pagination/ dashboard count queries written pre-Mongoose-7; upgrade sweeps that missed count calls because they are less frequent than find/findOne.","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"],"exampleFix":"// before\nUser.countDocuments({ active: true }, (err, count) => { ... });\n\n// after\nconst count = await User.countDocuments({ active: true });","handlingStrategy":"validation","validationCode":"const isFn = (a) => typeof a === 'function';\nif ([conditions, options].some(isFn)) {\n  throw new TypeError('countDocuments() is promise-only');\n}\nconst count = await User.countDocuments(conditions);","typeGuard":null,"tryCatchPattern":null,"preventionTips":["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"],"tags":["mongoose","callback-removed","promise","count","migration"],"backgroundTag":"mongoose-callback-removed","analyzedSha":"49cdab01366679723b487ecb754b38570f783289","analyzedAt":"2026-08-21T22:54:00.882Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}