{"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":"validation","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/3366c21a6311e02f1be91da982f9b93d3cce99a0/src/bulk/common.ts#L718-L754","documentation":"Thrown by FindOperators.updateOne() when the update document does not begin with an atomic operator (e.g. $set, $inc, $unset). The driver validates the first key of the document (or every doc if an array/pipeline of plain objects) starts with '$' via hasAtomicOperators(). MongoDB updateOne commands require an update operator or an aggregation pipeline; a plain replacement document is not accepted by this method (use replaceOne for that).","triggerScenarios":"Calling bulk.find(filter).updateOne(doc) where doc is a plain object like { field: value } without a leading $-prefixed key, or where doc is an empty object {}. Passing a replacement-style document to updateOne instead of replaceOne.","commonSituations":"Developers coming from SQL or other ORMs who construct the update body as a literal document; refactoring a replaceOne call into updateOne without adding $set; dynamically building update docs where the operator key is dropped by mistake.","solutions":["Wrap the replacement fields in { $set: { ... } } before passing to updateOne.","If you intend to replace the whole document, use bulk.find(filter).replaceOne(doc) instead.","For computed/aggregation updates, pass an array pipeline like [{ $set: { total: { $sum: [...] } } }] (the first key '$set' satisfies the check)."],"exampleFix":"// before\nbulk.find({ _id: 1 }).updateOne({ name: 'alice' });\n\n// after\nbulk.find({ _id: 1 }).updateOne({ $set: { name: 'alice' } });","handlingStrategy":"validation","validationCode":"import { hasAtomicOperators } from 'mongodb';\n\nfunction assertUpdateDoc(doc) {\n  if (!hasAtomicOperators(doc)) {\n    throw new TypeError('updateOne requires an atomic operator like $set');\n  }\n}","typeGuard":"function isAtomicUpdate(doc) {\n  return (\n    doc != null &&\n    typeof doc === 'object' &&\n    (Array.isArray(doc)\n      ? doc.length > 0\n      : Object.keys(doc).length > 0 && Object.keys(doc)[0][0] === '$')\n  );\n}","tryCatchPattern":"try {\n  bulk.find(filter).updateOne(updateDoc);\n} catch (e) {\n  if (e instanceof MongoInvalidArgumentError && /atomic operators/.test(e.message)) {\n    // wrap in $set and retry, or surface a clearer error\n  }\n  throw e;\n}","preventionTips":["Always wrap update fields in { $set: { ... } } by default.","Add a unit test asserting every updateOne call site passes an atomic-operator document.","Centralize update-document construction in a helper that enforces $-prefixed keys."],"tags":["bulk-write","update","validation","argument-error"],"analyzedSha":"3366c21a6311e02f1be91da982f9b93d3cce99a0","analyzedAt":"2026-08-04T13:40:15.335Z","schemaVersion":2}