{"record":{"id":"b4e11257b04cb62f","repo":"Automattic/mongoose","slug":"model-findoneandupdate-no-longer-accepts-a-callb","errorCode":null,"errorMessage":"Model.findOneAndUpdate() no longer accepts a callback","messagePattern":"Model\\.findOneAndUpdate\\(\\) no longer accepts a callback","errorType":"exception","errorClass":"MongooseError","httpStatus":null,"severity":"error","filePath":"lib/model.js","lineNumber":2430,"sourceCode":" * @param {boolean} [options.new=false] if true, return the modified document rather than the original\n * @param {object|string} [options.fields] Field selection. Equivalent to `.select(fields).findOneAndUpdate()`\n * @param {number} [options.maxTimeMS] puts a time limit on the query - requires mongodb >= 2.6.0\n * @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 [raw result from the MongoDB driver](https://mongodb.github.io/node-mongodb-native/7.0/interfaces/ModifyResult.html)\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 Tutorial https://mongoosejs.com/docs/tutorials/findoneandupdate.html\n * @see mongodb https://www.mongodb.com/docs/manual/reference/command/findAndModify/\n * @api public\n */\n\nModel.findOneAndUpdate = function(conditions, update, options) {\n  _checkContext(this, 'findOneAndUpdate');\n  if (typeof arguments[0] === 'function' || typeof arguments[1] === 'function' || typeof arguments[2] === 'function' || typeof arguments[3] === 'function') {\n    throw new MongooseError('Model.findOneAndUpdate() no longer accepts a callback');\n  }\n\n  let fields;\n  if (options) {\n    fields = options.fields || options.projection;\n  }\n\n  const mq = new this.Query({}, {}, this, this.$__collection);\n  mq.select(fields);\n\n  return mq.findOneAndUpdate(conditions, update, options);\n};\n\n/**\n * Issues a mongodb findOneAndUpdate command by a document's _id field.\n * `findByIdAndUpdate(id, ...)` is equivalent to `findOneAndUpdate({ _id: id }, ...)`.\n *\n * Finds a matching document, updates it according to the `update` arg,","sourceCodeStart":2412,"sourceCodeEnd":2448,"githubUrl":"https://github.com/Automattic/mongoose/blob/49cdab01366679723b487ecb754b38570f783289/lib/model.js#L2412-L2448","documentation":"Model.findOneAndUpdate(conditions, update, options) returns a Query; Mongoose 7 removed the callback form, so a function in any of the first four argument slots throws synchronously. Note the result shape: by default the query resolves to the document (or null), and `includeResultMetadata: true` returns the driver's raw result.","triggerScenarios":"`User.findOneAndUpdate({ _id }, { $set: { name } }, { new: true }, (err, doc) => ...)`; upsert helpers written callback-style; passing a callback in the `options` slot.","commonSituations":"Pre-Mongoose-7 upsert/patch handlers; confusion during upgrade because the old callback's `doc` is now the promise's resolved value (and null vs lastErrorObject semantics changed).","solutions":["Await it: `const doc = await User.findOneAndUpdate({ _id }, { $set: { name } }, { new: true });`","Use `.orFail()` or check for null instead of the old err-first null pattern","If you relied on the raw result, pass `includeResultMetadata: true` and read `.value`/`.lastErrorObject`","Sweep `findOneAndUpdate\\(` call sites for trailing function arguments"],"exampleFix":"// before\nUser.findOneAndUpdate({ _id: id }, { $set: { name } }, { new: true }, (err, doc) => {\n  if (err) throw err;\n  res.json(doc);\n});\n\n// after\nconst doc = await User.findOneAndUpdate({ _id: id }, { $set: { name } }, { new: true });\nres.json(doc);","handlingStrategy":"validation","validationCode":"const isFn = (a) => typeof a === 'function';\nif ([conditions, update, options].some(isFn)) {\n  throw new TypeError('findOneAndUpdate() is promise-only');\n}\nconst doc = await User.findOneAndUpdate(conditions, update, options);","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Decide result handling up front: default resolves the document (null if none); use includeResultMetadata for the raw result","Use .orFail() where the old code treated 'not found' as an error","TypeScript catches callback calls here at compile time — migrate types first, then runtime code"],"tags":["mongoose","callback-removed","promise","query","update","migration"],"backgroundTag":"mongoose-callback-removed","analyzedSha":"49cdab01366679723b487ecb754b38570f783289","analyzedAt":"2026-08-21T22:54:00.882Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}