{"record":{"id":"3f49ebc88ecb27b8","repo":"mongodb/node-mongodb-native","slug":"update-document-requires-atomic-operators-3f49eb","errorCode":null,"errorMessage":"Update document requires atomic operators","messagePattern":"Update document requires atomic operators","errorType":"exception","errorClass":"MongoInvalidArgumentError","httpStatus":null,"severity":"error","filePath":"src/operations/update.ts","lineNumber":146,"sourceCode":"      command.comment = options.comment;\n    }\n\n    return command;\n  }\n}\n\n/** @internal */\nexport class UpdateOneOperation extends UpdateOperation {\n  constructor(\n    ns: MongoDBCollectionNamespace,\n    filter: Document,\n    update: Document,\n    options: UpdateOptions\n  ) {\n    super(ns, [makeUpdateStatement(filter, update, { ...options, multi: false })], options);\n\n    if (!hasAtomicOperators(update, options)) {\n      throw new MongoInvalidArgumentError('Update document requires atomic operators');\n    }\n  }\n\n  override handleOk(\n    response: InstanceType<typeof this.SERVER_COMMAND_RESPONSE_TYPE>\n  ): UpdateResult {\n    const res = super.handleOk(response);\n\n    // @ts-expect-error Explain typing is broken\n    if (this.explain != null) return res;\n\n    if (res.code) throw new MongoServerError(res);\n    if (res.writeErrors) throw new MongoServerError(res.writeErrors[0]);\n\n    return {\n      acknowledged: this.writeConcern?.w !== 0,\n      modifiedCount: res.nModified ?? res.n,\n      upsertedId:","sourceCodeStart":128,"sourceCodeEnd":164,"githubUrl":"https://github.com/mongodb/node-mongodb-native/blob/dce7939f86fb283e167ad709955abedb7bf23124/src/operations/update.ts#L128-L164","documentation":"Thrown as a MongoInvalidArgumentError by the UpdateOneOperation constructor when the update document has no atomic operators (no key beginning with '$'). updateOne performs field-level modifications, so the update must contain operators like $set, $inc, $unset, or be an aggregation pipeline (which hasAtomicOperators treats via its options path). The check is `!hasAtomicOperators(update, options)` at update.ts:145.","triggerScenarios":"Calling collection.updateOne(filter, { field: value }) with a plain replacement document instead of { $set: { field: value } }. Any update whose first key does not start with '$' triggers this.","commonSituations":"Confusing updateOne with replaceOne; forgetting the $set wrapper; passing a full document intended for replaceOne into updateOne.","solutions":["Wrap changes in $set: collection.updateOne(filter, { $set: { field: value } }).","If you meant a full-document replace, use collection.replaceOne(filter, replacement).","For aggregation-based updates, pass a pipeline array of stages."],"exampleFix":"// before\nawait collection.updateOne({ _id: id }, { status: 'done' });\n\n// after\nawait collection.updateOne({ _id: id }, { $set: { status: 'done' } });","handlingStrategy":"validation","validationCode":"function hasAtomicOperators(doc: Record<string, unknown>): boolean {\n  const keys = Object.keys(doc);\n  return keys.length > 0 && keys[0].startsWith('$');\n}\nif (!hasAtomicOperators(update)) {\n  throw new TypeError('updateOne update must contain atomic operators; for full replace use replaceOne');\n}\nawait collection.updateOne(filter, update);","typeGuard":"function isUpdateDoc(v: unknown): v is Record<string, unknown> {\n  return typeof v === 'object' && v !== null && !Array.isArray(v)\n    && Object.keys(v).some(k => k.startsWith('$'));\n}","tryCatchPattern":null,"preventionTips":["Wrap field changes in $set (or another operator) for updateOne.","Use replaceOne for full-document replacement.","Preflight operator checks help when updates come from user input."],"tags":["validation","update-one","atomic-operators","update","argument"],"backgroundTag":null,"analyzedSha":"dce7939f86fb283e167ad709955abedb7bf23124","analyzedAt":"2026-08-11T04:54:53.215Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}