{"record":{"id":"b9f0852e51f2fb25","repo":"mongodb/node-mongodb-native","slug":"update-document-requires-atomic-operators","errorCode":null,"errorMessage":"Update document requires atomic operators","messagePattern":"Update document requires atomic operators","errorType":"exception","errorClass":"MongoInvalidArgumentError","httpStatus":null,"severity":"error","filePath":"src/bulk/common.ts","lineNumber":736,"sourceCode":"    this.bulkOperation = bulkOperation;\n  }\n\n  /** Add a multiple update operation to the bulk operation */\n  update(updateDocument: Document | Document[]): BulkOperationBase {\n    const currentOp = buildCurrentOp(this.bulkOperation);\n    return this.bulkOperation.addToOperationsList(\n      BatchType.UPDATE,\n      makeUpdateStatement(currentOp.selector, updateDocument, {\n        ...currentOp,\n        multi: true\n      })\n    );\n  }\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,","sourceCodeStart":718,"sourceCodeEnd":754,"githubUrl":"https://github.com/mongodb/node-mongodb-native/blob/dce7939f86fb283e167ad709955abedb7bf23124/src/bulk/common.ts#L718-L754","documentation":"Thrown as MongoInvalidArgumentError by FindOperators.updateOne() (src/bulk/common.ts:736) when the update document passed to bulkOp.find(filter).updateOne(doc) contains no MongoDB atomic operators ($set, $inc, $push, etc.). The driver requires update pipelines or update-operator documents for updateOne so it can build a correct OP_MSG update statement; a bare field document would be ambiguous and is rejected up front.","triggerScenarios":"Calling bulkOp.find({...}).updateOne({ field: value }) instead of bulkOp.find({...}).updateOne({ $set: { field: value } }). The guard is !hasAtomicOperators(updateDocument, this.bulkOperation.bsonOptions) at src/bulk/common.ts:735.","commonSituations":"Developers copy-paste a replaceOne-style document into updateOne, or come from a SQL background and try to set fields directly. Also happens when a variable that was supposed to hold {$set: {...}} is reassigned to a plain object, or when building the update dynamically and the $set key is dropped by a spread bug.","solutions":["Wrap the fields you want to change in an atomic operator, most commonly $set: updateOne({ $set: { status: 'active' } }).","If you actually intend to replace the whole document, use replaceOne(...) instead of updateOne(...).","If you want aggregation-style updates, pass an array pipeline: updateOne([{ $set: { total: { $sum: ['$a','$b'] } } }]) (pipelines are detected as operator-bearing).","Double-check any dynamic builder that constructs the update object to ensure the $-prefixed key survives the spread/merge."],"exampleFix":"// before\nbulk.find({ _id: 1 }).updateOne({ status: 'active' });\n\n// after\nbulk.find({ _id: 1 }).updateOne({ $set: { status: 'active' } });","handlingStrategy":"validation","validationCode":"import { hasAtomicOperators } from 'mongodb'; // internal helper, mirror locally if not exported\n\nfunction isValidUpdateOne(doc: unknown): boolean {\n  return (\n    doc != null &&\n    typeof doc === 'object' &&\n    (Array.isArray(doc)\n      ? doc.every(d => hasAtomicOperators(d))\n      : hasAtomicOperators(doc))\n  );\n}\n\nif (!isValidUpdateOne(updateDoc)) {\n  throw new Error('updateOne body must use atomic operators like $set');\n}","typeGuard":"function isUpdateBody(doc: unknown): doc is Record<string, unknown> | Record<string, unknown>[] {\n  if (doc == null || typeof doc !== 'object') return false;\n  if (Array.isArray(doc)) return doc.every(d => isUpdateBody(d));\n  return Object.keys(doc as Record<string, unknown>).some(k => k.startsWith('$'));\n}","tryCatchPattern":"try {\n  bulk.find(filter).updateOne(updateDoc);\n} catch (e) {\n  if (e instanceof MongoInvalidArgumentError && /atomic operators/.test(e.message)) {\n    updateDoc = { $set: updateDoc };\n    bulk.find(filter).updateOne(updateDoc);\n  } else throw e;\n}","preventionTips":["Always wrap update fields in $set/$inc/etc. for updateOne/updateMany/update.","Keep replace-style builders separate from update-style builders to avoid leaking.","Add a unit test asserting updateOne bodies start with a $-prefixed key.","When constructing updates dynamically, type the result as Record<`$${string}`, unknown>."],"tags":["bulk-write","update","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"}