{"record":{"id":"70ab1c23ba82265d","repo":"Automattic/mongoose","slug":"query-prototype-exec-no-longer-accepts-a-callbac","errorCode":null,"errorMessage":"Query.prototype.exec() no longer accepts a callback","messagePattern":"Query\\.prototype\\.exec\\(\\) no longer accepts a callback","errorType":"exception","errorClass":"MongooseError","httpStatus":null,"severity":"error","filePath":"lib/query.js","lineNumber":4746,"sourceCode":"  return isPathSelectedInclusive(this._fields, path);\n};\n\n/**\n * Executes the query\n *\n * #### Example:\n *\n *     const promise = query.exec();\n *     const promise = query.exec('update');\n *\n * @param {string|Function} [operation]\n * @return {Promise}\n * @api public\n */\n\nQuery.prototype.exec = async function exec(op) {\n  if (typeof op === 'function' || (arguments.length >= 2 && typeof arguments[1] === 'function')) {\n    throw new MongooseError('Query.prototype.exec() no longer accepts a callback');\n  }\n\n  this._validateOp();\n  if (typeof op === 'string') {\n    this.op = op;\n  }\n\n  if (this.op == null) {\n    throw new MongooseError('Query must have `op` before executing');\n  }\n  if (this.model == null) {\n    throw new MongooseError('Query must have an associated model before executing');\n  }\n\n  const thunk = opToThunk.get(this.op);\n  if (!thunk) {\n    throw new MongooseError('Query has invalid `op`: \"' + this.op + '\"');\n  }","sourceCodeStart":4728,"sourceCodeEnd":4764,"githubUrl":"https://github.com/Automattic/mongoose/blob/49cdab01366679723b487ecb754b38570f783289/lib/query.js#L4728-L4764","documentation":"Mongoose 7 removed callback support from all query methods, and Query.prototype.exec() now throws if its first argument is a function or a function is passed as a second argument. exec() is async and returns a promise; the old `query.exec('update', callback)` and `query.exec(callback)` patterns are hard errors rather than deprecation warnings.","triggerScenarios":"`query.exec(callback)`, `query.exec('find', callback)`, or `.then()`-less callback chains left over from Mongoose <=6; libraries that probe arguments and forward callbacks into exec.","commonSituations":"Upgrading an app from Mongoose 5/6 to 7+; legacy codebases with hundreds of `exec(cb)` sites; old middleware that wraps exec with a callback interface.","solutions":["Replace `query.exec(cb)` with `await query.exec()` or `query.exec().then(...)`.","Replace `query.exec('find', cb)` with `query.find(); await query.exec()` or `query.find().exec()` (chainable exec takes no op).","Use the Mongoose 7 migration guide and grep for `exec(function` / `exec(cb` to find every remaining site.","If you must keep a callback API for downstream code, wrap the promise yourself in one adapter instead of passing callbacks to Mongoose."],"exampleFix":"// before (Mongoose 6)\nModel.find({}).sort({ name: 1 }).exec((err, docs) => { ... });\n\n// after (Mongoose 7+)\nconst docs = await Model.find({}).sort({ name: 1 }).exec();","handlingStrategy":"type-guard","validationCode":"if (typeof arguments[0] === 'function' || typeof arguments[1] === 'function') { throw new TypeError('exec() is promise-only in Mongoose 7+'); }","typeGuard":"const argsContainCallback = (...args) => args.some(a => typeof a === 'function');","tryCatchPattern":"try { await query.exec(); } catch (err) { if (err instanceof mongoose.Error && /no longer accepts a callback/.test(err.message)) { /* strip callback args, switch to await */ } throw err; }","preventionTips":["Grep for 'exec(' plus function args during Mongoose 7 upgrades.","Use async/await consistently for all queries.","Wrap callback-style consumers behind one promise adapter."],"tags":["mongoose","callback-removed","migration","promise"],"backgroundTag":"mongoose-callbacks-removed","analyzedSha":"49cdab01366679723b487ecb754b38570f783289","analyzedAt":"2026-08-21T22:54:00.882Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}