{"id":"991e591036f57ad4","repo":"mongodb/node-mongodb-native","slug":"replacement-document-must-not-contain-atomic-opera","errorCode":null,"errorMessage":"Replacement document must not contain atomic operators","messagePattern":"Replacement document must not contain atomic operators","errorType":"exception","errorClass":"MongoInvalidArgumentError","httpStatus":null,"severity":"error","filePath":"src/operations/find_and_modify.ts","lineNumber":240,"sourceCode":"/** @internal */\nexport class FindOneAndReplaceOperation extends FindAndModifyOperation {\n  private replacement: Document;\n  constructor(\n    collection: Collection,\n    filter: Document,\n    replacement: Document,\n    options: FindOneAndReplaceOptions\n  ) {\n    if (filter == null || typeof filter !== 'object') {\n      throw new MongoInvalidArgumentError('Argument \"filter\" must be an object');\n    }\n\n    if (replacement == null || typeof replacement !== 'object') {\n      throw new MongoInvalidArgumentError('Argument \"replacement\" must be an object');\n    }\n\n    if (hasAtomicOperators(replacement)) {\n      throw new MongoInvalidArgumentError('Replacement document must not contain atomic operators');\n    }\n\n    super(collection, filter, options);\n    this.replacement = replacement;\n  }\n\n  override buildCommandDocument(\n    connection: Connection,\n    session?: ClientSession\n  ): Document & FindAndModifyCmdBase {\n    const document = super.buildCommandDocument(connection, session);\n    document.update = this.replacement;\n    configureFindAndModifyCmdBaseUpdateOpts(document, this.options);\n    return document;\n  }\n}\n\n/** @internal */","sourceCodeStart":222,"sourceCodeEnd":258,"githubUrl":"https://github.com/mongodb/node-mongodb-native/blob/3366c21a6311e02f1be91da982f9b93d3cce99a0/src/operations/find_and_modify.ts#L222-L258","documentation":"FindOneAndReplaceOperation's constructor (src/operations/find_and_modify.ts:240) calls hasAtomicOperators(replacement) and throws a MongoInvalidArgumentError if true. A replacement document must be a plain document — it fully substitutes the matched document — so update operators like $set, $inc, $unset are forbidden. This is a common confusion between findOneAndReplace and findOneAndUpdate.","triggerScenarios":"Calling collection.findOneAndReplace(filter, { $set: { field: value } }) or any replacement containing operators prefixed with $.","commonSituations":"Developers defaulting to $set out of habit and reaching for findOneAndReplace instead of findOneAndUpdate, or copy-pasting an update document into a replace call.","solutions":["Use collection.findOneAndUpdate(filter, { $set: { ... } }) when you need atomic operators.","If you truly want findOneAndReplace, pass a full replacement document without any $ operators.","Audit the document to ensure no keys start with '$' before calling findOneAndReplace."],"exampleFix":"// before\nawait collection.findOneAndReplace({ _id }, { $set: { status: 'done' } });\n\n// after\nawait collection.findOneAndUpdate({ _id }, { $set: { status: 'done' } });\n// or full replace:\nawait collection.findOneAndReplace({ _id }, { status: 'done', createdAt: new Date() });","handlingStrategy":"validation","validationCode":"function hasAtomicOperators(doc: Record<string, unknown>): boolean {\n  return Object.keys(doc).some(k => k.startsWith('$'));\n}\nif (hasAtomicOperators(replacement)) {\n  throw new TypeError('Use findOneAndUpdate for atomic operators, not findOneAndReplace');\n}","typeGuard":"const isAtomicUpdate = (doc: Record<string, unknown>): boolean =>\n  Object.keys(doc).some(k => k.startsWith('$'));","tryCatchPattern":null,"preventionTips":["Use findOneAndUpdate for $set/$inc/$push; use findOneAndReplace only for full-document replacement.","Scan replacement keys for a leading '$' before calling findOneAndReplace.","Document the distinction in your codebase to prevent copy-paste mistakes."],"tags":["argument-validation","find-one-and-modify","atomic-operators","client-side"],"analyzedSha":"3366c21a6311e02f1be91da982f9b93d3cce99a0","analyzedAt":"2026-08-04T13:40:15.335Z","schemaVersion":2}