{"record":{"id":"3995df0a45f62206","repo":"Automattic/mongoose","slug":"model-deletemany-no-longer-accepts-a-callback","errorCode":null,"errorMessage":"Model.deleteMany() no longer accepts a callback","messagePattern":"Model\\.deleteMany\\(\\) no longer accepts a callback","errorType":"exception","errorClass":"MongooseError","httpStatus":null,"severity":"error","filePath":"lib/model.js","lineNumber":2083,"sourceCode":" *     await Character.deleteMany({ name: /Stark/, age: { $gte: 18 } }); // returns {deletedCount: x} where x is the number of documents deleted.\n *\n * #### Note:\n *\n * This function triggers `deleteMany` query hooks. Read the\n * [middleware docs](https://mongoosejs.com/docs/middleware.html#naming) to learn more.\n *\n * @param {object} conditions\n * @param {object} [options] optional see [`Query.prototype.setOptions()`](https://mongoosejs.com/docs/api/query.html#Query.prototype.setOptions())\n * @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.\n * @return {Query}\n * @api public\n */\n\nModel.deleteMany = function deleteMany(conditions, options) {\n  _checkContext(this, 'deleteMany');\n\n  if (typeof arguments[0] === 'function' || typeof arguments[1] === 'function' || typeof arguments[2] === 'function') {\n    throw new MongooseError('Model.deleteMany() no longer accepts a callback');\n  }\n\n  const mq = new this.Query({}, {}, this, this.$__collection);\n  mq.setOptions(options);\n\n  return mq.deleteMany(conditions);\n};\n\n/**\n * Finds documents.\n *\n * Mongoose casts the `filter` to match the model's schema before the command is sent.\n * See our [query casting tutorial](https://mongoosejs.com/docs/tutorials/query_casting.html) for\n * more information on how Mongoose casts `filter`.\n *\n * #### Example:\n *\n *     // find all documents","sourceCodeStart":2065,"sourceCodeEnd":2101,"githubUrl":"https://github.com/Automattic/mongoose/blob/49cdab01366679723b487ecb754b38570f783289/lib/model.js#L2065-L2101","documentation":"Model.deleteMany(conditions, options) deletes all matching documents via a Query. As with every query static, Mongoose 7 dropped callback support: passing a function in any of the first three positional slots throws before the Query is even built.","triggerScenarios":"`User.deleteMany({ inactive: true }, cb)`; cleanup jobs from the callback era; `User.deleteMany(cb)` intending delete-all.","commonSituations":"Post-migration leftovers; bulk-cleanup cron scripts that were never touched during the upgrade.","solutions":["Await it: `const { deletedCount } = await User.deleteMany({ inactive: true });`","Migrate error handling to try/catch around the await","Sweep for `\\.deleteMany\\(` with function arguments repo-wide"],"exampleFix":"// before\nUser.deleteMany({ inactive: true }, (err) => { ... });\n\n// after\nconst { deletedCount } = await User.deleteMany({ inactive: true });","handlingStrategy":"validation","validationCode":"const isFn = (a) => typeof a === 'function';\nif ([conditions, options].some(isFn)) throw new TypeError('deleteMany() is promise-only');\nconst { deletedCount } = await User.deleteMany(conditions, options);","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Convert bulk-cleanup jobs to await-based loops; old callback counters hide these errors until runtime","Keep the migration grep list for deletes next to finds so none are missed"],"tags":["mongoose","callback-removed","promise","delete","migration"],"backgroundTag":"mongoose-callback-removed","analyzedSha":"49cdab01366679723b487ecb754b38570f783289","analyzedAt":"2026-08-21T22:54:00.882Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}