{"record":{"id":"3a81ee70b325f026","repo":"Automattic/mongoose","slug":"query-prototype-findoneandreplace-no-longer-acce","errorCode":null,"errorMessage":"Query.prototype.findOneAndReplace() no longer accepts a callback","messagePattern":"Query\\.prototype\\.findOneAndReplace\\(\\) no longer accepts a callback","errorType":"exception","errorClass":"MongooseError","httpStatus":null,"severity":"error","filePath":"lib/query.js","lineNumber":3789,"sourceCode":" * @param {boolean} [options.new=false] By default, `findOneAndUpdate()` returns the document as it was **before** `update` was applied. If you set `new: true`, `findOneAndUpdate()` will instead give you the object after `update` was applied. **Deprecated:** Use `returnDocument: 'after'` instead of `new: true`, or `returnDocument: 'before'` instead of `new: false`.\n * @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 {boolean} [options.returnOriginal=null] An alias for the `new` option. `returnOriginal: false` is equivalent to `new: true`. **Deprecated:** Use `returnDocument: 'after'` instead of `returnOriginal: false`, or `returnDocument: 'before'` instead of `returnOriginal: true`.\n * @param {'before'|'after'} [options.returnDocument='before'] Has two possible values, `'before'` and `'after'`. By default, it will return the document before the update was applied.\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.requireFilter=false] If true, throws an error if the filter is empty (`{}`)\n * @return {Query} this\n * @api public\n */\n\nQuery.prototype.findOneAndReplace = function(filter, replacement, options) {\n  if (typeof filter === 'function' ||\n      typeof replacement === 'function' ||\n      typeof options === 'function' ||\n      typeof arguments[4] === 'function') {\n    throw new MongooseError('Query.prototype.findOneAndReplace() no longer accepts a callback');\n  }\n\n  this.op = 'findOneAndReplace';\n  this._validate();\n\n  if (canMerge(filter)) {\n    this.merge(filter);\n  } else if (filter != null) {\n    this.error(\n      new ObjectParameterError(filter, 'filter', 'findOneAndReplace')\n    );\n  }\n\n  if (replacement != null) {\n    this._mergeUpdate(replacement);\n  }\n\n  options = options || {};","sourceCodeStart":3771,"sourceCodeEnd":3807,"githubUrl":"https://github.com/Automattic/mongoose/blob/49cdab01366679723b487ecb754b38570f783289/lib/query.js#L3771-L3807","documentation":"Query.prototype.findOneAndReplace(filter, replacement, options) throws when filter, replacement, or options is a function, or when a fifth positional argument (arguments[4]) is a function. Note the guard checks arguments[4] rather than arguments[3], so a callback passed as a fourth argument is not caught by this specific check — functions in the named parameter slots are what reliably trigger it. This is part of the Mongoose 7 removal of callback execution.","triggerScenarios":"Model.findOneAndReplace(filter, replacement, cb) with the callback in the options slot; .findOneAndReplace(filter, cb) with the callback in the replacement slot; legacy code passing a fifth-argument callback.","commonSituations":"Document-replacement flows (config documents, upsert-by-replace) written for Mongoose 6; major upgrades; legacy wrappers that append callbacks positionally.","solutions":["Use await: const doc = await Model.findOneAndReplace(filter, replacement, { returnDocument: 'after' })","Wrap in try/catch; the promise resolves null when no document matched","Strip callbacks from all findOneAndReplace call sites when migrating to mongoose 7+"],"exampleFix":"// before\nModel.findOneAndReplace({ key: 'theme' }, { key: 'theme', color: 'blue' }, (err, doc) => {\n  if (err) return next(err);\n  res.json(doc);\n});\n\n// after\ntry {\n  const doc = await Model.findOneAndReplace(\n    { key: 'theme' },\n    { key: 'theme', color: 'blue' },\n    { returnDocument: 'after' }\n  );\n  res.json(doc);\n} catch (err) {\n  next(err);\n}","handlingStrategy":"validation","validationCode":"function findOneAndReplaceSafe(model, filter, replacement, options) {\n  if ([filter, replacement, options].some(v => typeof v === 'function')) {\n    throw new Error('findOneAndReplace() is promise-only in Mongoose 7+');\n  }\n  return model.findOneAndReplace(filter, replacement, options);\n}","typeGuard":"const isLegacyCallback = (v) => typeof v === 'function';","tryCatchPattern":"try {\n  const doc = await Model.findOneAndReplace(filter, replacement, { returnDocument: 'after' });\n} catch (err) {\n  if (err?.message?.includes('no longer accepts a callback')) {\n    // fix the findOneAndReplace call site that still passes a callback\n  }\n  throw err;\n}","preventionTips":["Await findOneAndReplace with an explicit options object","Remember the replacement must be a plain document, not an update operator object","Note: a 4th-argument function is not caught by this specific guard — do not rely on positional extras"],"tags":["mongoose","callback","promise","migration","breaking-change","replace"],"backgroundTag":"legacy-callback-removed","analyzedSha":"49cdab01366679723b487ecb754b38570f783289","analyzedAt":"2026-08-21T22:54:00.882Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}