{"record":{"id":"c63ddc841faa3019","repo":"mongodb/node-mongodb-native","slug":"replacement-document-must-not-use-atomic-operators","errorCode":null,"errorMessage":"Replacement document must not use atomic operators","messagePattern":"Replacement document must not use atomic operators","errorType":"exception","errorClass":"MongoInvalidArgumentError","httpStatus":null,"severity":"error","filePath":"src/bulk/common.ts","lineNumber":749,"sourceCode":"  }\n\n  /** Add a single update operation to the bulk operation */\n  updateOne(updateDocument: Document | Document[]): BulkOperationBase {\n    if (!hasAtomicOperators(updateDocument, this.bulkOperation.bsonOptions)) {\n      throw new MongoInvalidArgumentError('Update document requires atomic operators');\n    }\n\n    const currentOp = buildCurrentOp(this.bulkOperation);\n    return this.bulkOperation.addToOperationsList(\n      BatchType.UPDATE,\n      makeUpdateStatement(currentOp.selector, updateDocument, { ...currentOp, multi: false })\n    );\n  }\n\n  /** Add a replace one operation to the bulk operation */\n  replaceOne(replacement: Document): BulkOperationBase {\n    if (hasAtomicOperators(replacement)) {\n      throw new MongoInvalidArgumentError('Replacement document must not use atomic operators');\n    }\n\n    const currentOp = buildCurrentOp(this.bulkOperation);\n    return this.bulkOperation.addToOperationsList(\n      BatchType.UPDATE,\n      makeUpdateStatement(currentOp.selector, replacement, { ...currentOp, multi: false })\n    );\n  }\n\n  /** Add a delete one operation to the bulk operation */\n  deleteOne(): BulkOperationBase {\n    const currentOp = buildCurrentOp(this.bulkOperation);\n    return this.bulkOperation.addToOperationsList(\n      BatchType.DELETE,\n      makeDeleteStatement(currentOp.selector, { ...currentOp, limit: 1 })\n    );\n  }\n","sourceCodeStart":731,"sourceCodeEnd":767,"githubUrl":"https://github.com/mongodb/node-mongodb-native/blob/dce7939f86fb283e167ad709955abedb7bf23124/src/bulk/common.ts#L731-L767","documentation":"Thrown as MongoInvalidArgumentError by FindOperators.replaceOne() (src/bulk/common.ts:749) when the replacement document contains MongoDB atomic operators ($set, $inc, etc.). replaceOne performs a full-document replacement, so the body must be a plain document with the new field values; mixing in $-operators would be rejected by the server and is caught client-side first.","triggerScenarios":"Calling bulkOp.find(filter).replaceOne({ $set: { a: 1 } }) instead of a plain replacement like replaceOne({ a: 1 }). Guarded by hasAtomicOperators(replacement) at src/bulk/common.ts:748.","commonSituations":"Developers switch from updateOne to replaceOne without removing the $set wrapper, or share a helper that always wraps fields in $set across both update and replace code paths. Also arises from copy-pasting an update document into a replace call.","solutions":["Strip the $set wrapper and pass the literal fields: replaceOne({ a: 1, b: 2 }).","If you actually want partial updates, switch the call to updateOne/update (which require the operators).","Audit shared builders so they do not unconditionally prefix field keys with $ operators."],"exampleFix":"// before\nbulk.find({ _id: 1 }).replaceOne({ $set: { name: 'Sam' } });\n\n// after\nbulk.find({ _id: 1 }).replaceOne({ name: 'Sam' });","handlingStrategy":"validation","validationCode":"function isPlainReplacement(doc: unknown): boolean {\n  if (doc == null || typeof doc !== 'object' || Array.isArray(doc)) return false;\n  return !Object.keys(doc as Record<string, unknown>).some(k => k.startsWith('$'));\n}\n\nif (!isPlainReplacement(replacement)) {\n  throw new Error('replaceOne body must be a plain document, no $-operators');\n}","typeGuard":"function isReplacementBody(doc: unknown): doc is Record<string, unknown> {\n  if (doc == null || typeof doc !== 'object' || Array.isArray(doc)) return false;\n  return !Object.keys(doc).some(k => k.startsWith('$'));\n}","tryCatchPattern":"try {\n  bulk.find(filter).replaceOne(replacement);\n} catch (e) {\n  if (e instanceof MongoInvalidArgumentError && /must not use atomic operators/.test(e.message)) {\n    // strip $set wrapper if you accidentally wrapped a replacement\n    const inner = (replacement as any).$set ?? replacement;\n    bulk.find(filter).replaceOne(inner);\n  } else throw e;\n}","preventionTips":["Treat replaceOne bodies as plain new documents, never as $-operator documents.","Do not share a single field-builder between update and replace code paths.","Add a unit test asserting replaceOne bodies contain no $-prefixed keys.","When migrating updateOne -> replaceOne, remember to drop $set."],"tags":["bulk-write","replace","atomic-operators","validation"],"backgroundTag":null,"analyzedSha":"dce7939f86fb283e167ad709955abedb7bf23124","analyzedAt":"2026-08-11T04:54:53.215Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}