{"record":{"id":"557b15d4a774d536","repo":"Automattic/mongoose","slug":"model-findoneandreplace-no-longer-accepts-a-call","errorCode":null,"errorMessage":"Model.findOneAndReplace() no longer accepts a callback","messagePattern":"Model\\.findOneAndReplace\\(\\) no longer accepts a callback","errorType":"exception","errorClass":"MongooseError","httpStatus":null,"severity":"error","filePath":"lib/model.js","lineNumber":2644,"sourceCode":" * @param {object} [options.lean] if truthy, mongoose will return the document as a plain JavaScript object rather than a mongoose document. See [`Query.lean()`](https://mongoosejs.com/docs/api/query.html#Query.prototype.lean()) and [the Mongoose lean tutorial](https://mongoosejs.com/docs/tutorials/lean.html).\n * @param {ClientSession} [options.session=null] The session associated with this query. See [transactions docs](https://mongoosejs.com/docs/transactions.html).\n * @param {boolean|'throw'} [options.strict] overwrites the schema's [strict mode option](https://mongoosejs.com/docs/guide.html#strict)\n * @param {boolean} [options.timestamps=null] If set to `false` and [schema-level timestamps](https://mongoosejs.com/docs/guide.html#timestamps) are enabled, skip timestamps for this update. Note that this allows you to overwrite timestamps. Does nothing if schema-level timestamps are not set.\n * @param {object|string|string[]} [options.projection=null] optional fields to return, see [`Query.prototype.select()`](https://mongoosejs.com/docs/api/query.html#Query.prototype.select())\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.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 {object|string} [options.select] sets the document fields to return.\n * @param {number} [options.maxTimeMS] puts a time limit on the query - requires mongodb >= 2.6.0\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.findOneAndReplace = function(filter, replacement, options) {\n  _checkContext(this, 'findOneAndReplace');\n\n  if (typeof arguments[0] === 'function' || typeof arguments[1] === 'function' || typeof arguments[2] === 'function' || typeof arguments[3] === 'function') {\n    throw new MongooseError('Model.findOneAndReplace() no longer accepts a callback');\n  }\n\n  let fields;\n  if (options) {\n    fields = options.select;\n    options.select = undefined;\n  }\n\n  const mq = new this.Query({}, {}, this, this.$__collection);\n  mq.select(fields);\n\n  return mq.findOneAndReplace(filter, replacement, options);\n};\n\n/**\n * Shortcut for saving one or more documents to the database.\n * `MyModel.create(docs)` does `new MyModel(doc).save()` for every doc in\n * docs.","sourceCodeStart":2626,"sourceCodeEnd":2662,"githubUrl":"https://github.com/Automattic/mongoose/blob/49cdab01366679723b487ecb754b38570f783289/lib/model.js#L2626-L2662","documentation":"Mongoose 7 removed callback support from all Model and Query APIs; findOneAndReplace() returns a Query (thenable) only. The function throws this MongooseError synchronously if any of arguments[0] through arguments[3] is a function, covering filter, replacement, options, and the legacy trailing callback position.","triggerScenarios":"Calling Model.findOneAndReplace(filter, replacement, options, callback) or Model.findOneAndReplace(filter, replacement, callback) with pre-7 code. Also triggered when a function value ends up in the filter or replacement slot through argument misordering.","commonSituations":"Migrating from Mongoose 6 to 7/8; reusing callback-style snippets from old documentation or answers; wrapper functions that forward variadic arguments including callbacks.","solutions":["Replace the callback with await: const doc = await Model.findOneAndReplace(filter, replacement, { returnDocument: 'after' }); in try/catch","Or use .then()/.catch() on the returned Query","Grep and migrate all callback-based Model methods together (the Mongoose 6 to 7 migration guide lists them)","Pin mongoose@6 temporarily if needed, then migrate","Use TypeScript to catch removed overloads at compile time"],"exampleFix":"// before\nUser.findOneAndReplace({ _id }, { name: 'a' }, { new: true }, (err, doc) => { ... });\n\n// after\nconst doc = await User.findOneAndReplace({ _id }, { name: 'a' }, { returnDocument: 'after' });","handlingStrategy":"validation","validationCode":"function assertNoCallbacks(fnName, args) {\n  const i = args.findIndex(a => typeof a === 'function');\n  if (i !== -1) throw new TypeError(`${fnName}: callbacks removed in Mongoose 7; use await`);\n}\n// assertNoCallbacks('findOneAndReplace', [filter, replacement, options]);","typeGuard":null,"tryCatchPattern":"try {\n  const doc = await Model.findOneAndReplace(filter, replacement, { returnDocument: 'after' });\n} catch (err) {\n  if (/no longer accepts a callback/.test(err.message)) { /* migrate call site */ }\n  else throw err;\n}","preventionTips":["Note options changed too: { new: true } became returnDocument: 'after' when migrating this method","Use TypeScript to catch removed overloads","Batch-migrate all findAndModify-style methods in one pass to avoid partial upgrades"],"tags":["mongoose","callback","promise","migration","api-change","replace"],"backgroundTag":"callback-to-promise-migration","analyzedSha":"49cdab01366679723b487ecb754b38570f783289","analyzedAt":"2026-08-21T22:54:00.882Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}