{"record":{"id":"4cbcbc328794d5c9","repo":"Automattic/mongoose","slug":"model-findbyidandupdate-no-longer-accepts-a-call","errorCode":null,"errorMessage":"Model.findByIdAndUpdate() no longer accepts a callback","messagePattern":"Model\\.findByIdAndUpdate\\(\\) no longer accepts a callback","errorType":"exception","errorClass":"MongooseError","httpStatus":null,"severity":"error","filePath":"lib/model.js","lineNumber":2510,"sourceCode":" * @param {object|string} [options.sort] if multiple docs are found by the conditions, sets the sort order to choose which doc to update.\n * @param {boolean} [options.runValidators] if true, runs [update validators](https://mongoosejs.com/docs/validation.html#update-validators) on this command. Update validators validate the update operation against the model's schema\n * @param {boolean} [options.setDefaultsOnInsert=true] If `setDefaultsOnInsert` and `upsert` are true, mongoose will apply the [defaults](https://mongoosejs.com/docs/defaults.html) specified in the model's schema if a new document is created\n * @param {boolean} [options.includeResultMetadata] if true, returns the full [ModifyResult from the MongoDB driver](https://mongodb.github.io/node-mongodb-native/7.0/interfaces/ModifyResult.html) rather than just the document\n * @param {boolean} [options.upsert=false] if true, and no documents found, insert a new document\n * @param {boolean} [options.new=false] if true, return the modified document rather than the original\n * @param {object|string} [options.select] sets the document fields to return.\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 * @param {boolean} [options.overwriteDiscriminatorKey=false] Mongoose removes discriminator key updates from `update` by default, set `overwriteDiscriminatorKey` to `true` to allow updating the discriminator key\n * @return {Query}\n * @see Model.findOneAndUpdate https://mongoosejs.com/docs/api/model.html#Model.findOneAndUpdate()\n * @see mongodb https://www.mongodb.com/docs/manual/reference/command/findAndModify/\n * @api public\n */\n\nModel.findByIdAndUpdate = function(id, update, options) {\n  _checkContext(this, 'findByIdAndUpdate');\n  if (typeof arguments[0] === 'function' || typeof arguments[1] === 'function' || typeof arguments[2] === 'function' || typeof arguments[3] === 'function') {\n    throw new MongooseError('Model.findByIdAndUpdate() no longer accepts a callback');\n  }\n\n  // if a model is passed in instead of an id\n  if (id instanceof Document) {\n    id = id._doc._id;\n  }\n\n  return this.findOneAndUpdate.call(this, { _id: id }, update, options);\n};\n\n/**\n * Issue a MongoDB `findOneAndDelete()` command.\n *\n * Finds a matching document, removes it, and returns the found document (if any).\n *\n * This function triggers the following middleware.\n *\n * - `findOneAndDelete()`","sourceCodeStart":2492,"sourceCodeEnd":2528,"githubUrl":"https://github.com/Automattic/mongoose/blob/49cdab01366679723b487ecb754b38570f783289/lib/model.js#L2492-L2528","documentation":"Mongoose 7 removed callback support from all Model and Query APIs; findByIdAndUpdate() now returns a Query (thenable) only. The function inspects arguments[0] through arguments[3] and throws this MongooseError synchronously if any of them is a function. The check covers every position because a callback was previously allowed as the last argument (after id, update, options).","triggerScenarios":"Calling Model.findByIdAndUpdate(id, update, options, callback) with Mongoose 6-style callback code, or Model.findByIdAndUpdate(id, update, callback) where the callback lands in the options slot (arguments[2]). Also triggered by argument-order bugs where a function is passed as id or update.","commonSituations":"Upgrading an app or a dependency from Mongoose 6 (or earlier) to Mongoose 7/8 without migrating call sites; copy-pasting code from old tutorials or Stack Overflow answers that use function (err, doc) {} style; shared middleware/helpers written for the callback API.","solutions":["Remove the callback and use await: const doc = await Model.findByIdAndUpdate(id, update, { new: true });, with try/catch for the error path","Or chain .then()/.catch() on the returned Query: Model.findByIdAndUpdate(id, update).then(doc => ...).catch(err => ...)","Grep the codebase for callback patterns during upgrade: rg \"findByIdAndUpdate\\\\([^)]*,\\\\s*(function|\\\\([^)]*\\\\)\\\\s*=>)\" and migrate every hit (Mongoose 6 to 7 migration guide has a checklist)","If an immediate rewrite is impossible, temporarily pin mongoose@6 and schedule the migration; do not shim callbacks by wrapping the returned Query","If using TypeScript, rely on the Mongoose 7+ type definitions: callback overloads were deleted, so tsc flags these call sites at compile time"],"exampleFix":"// before (Mongoose 6)\nUser.findByIdAndUpdate(id, { name: 'a' }, { new: true }, (err, doc) => {\n  if (err) return handleError(err);\n  console.log(doc);\n});\n\n// after (Mongoose 7/8)\ntry {\n  const doc = await User.findByIdAndUpdate(id, { name: 'a' }, { new: true });\n  console.log(doc);\n} catch (err) {\n  handleError(err);\n}","handlingStrategy":"validation","validationCode":"// Guard shared call sites / migration shims against callback args\nfunction assertNoCallbacks(fnName, args) {\n  const i = args.findIndex(a => typeof a === 'function');\n  if (i !== -1) {\n    throw new TypeError(`${fnName}: arg ${i + 1} is a function; use await (Mongoose 7+ is promise-only)`);\n  }\n}\n// assertNoCallbacks('findByIdAndUpdate', [id, update, options]);","typeGuard":null,"tryCatchPattern":"try {\n  const doc = await Model.findByIdAndUpdate(id, update, { new: true });\n} catch (err) {\n  if (err instanceof mongoose.MongooseError && /no longer accepts a callback/.test(err.message)) {\n    // legacy callback call site slipped through the migration\n    throw new Error('Migrate this call site to await/then', { cause: err });\n  }\n  throw err;\n}","preventionTips":["Run the Mongoose 6 to 7 migration checklist and grep for ', function (' and arrow callbacks near Model method calls","Use TypeScript: Mongoose 7+ typings have no callback overloads, so tsc flags every legacy call","Standardize on async/await in lint rules (e.g. no-passing-callbacks custom rule / prefer-async) so callbacks cannot creep back"],"tags":["mongoose","callback","promise","migration","api-change","findandmodify"],"backgroundTag":"callback-to-promise-migration","analyzedSha":"49cdab01366679723b487ecb754b38570f783289","analyzedAt":"2026-08-21T22:54:00.882Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}