{"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/3366c21a6311e02f1be91da982f9b93d3cce99a0/src/operations/update.ts#L128-L164","documentation":"Thrown by UpdateOneOperation's constructor when the update argument to collection.updateOne() has no atomic operators. Identical guard and rationale as findOneAndUpdate (error 300): updateOne must mutate via $set/$inc/etc. or an aggregation pipeline; a plain document is rejected client-side. MongoInvalidArgumentError.","triggerScenarios":"collection.updateOne(filter, { field: value }) with no $-operator keys; passing a replacement-style document where an update document is required; argument-order mistakes placing a non-operator object in the update slot.","commonSituations":"Confusing updateOne with replaceOne; dynamic update builders yielding a bare object on some code path; migrating field assignment code into MongoDB without wrapping in $set.","solutions":["Wrap changes in $set / $inc / $push etc.: { $set: { field: value } }.","If you meant whole-document replacement, use collection.replaceOne(filter, replacement).","For conditional/expressive updates pass an aggregation pipeline array.","Verify the call signature: updateOne(filter, update, options)."],"exampleFix":"// before\nawait collection.updateOne({ _id: 1 }, { status: 'active' });\n// after\nawait collection.updateOne({ _id: 1 }, { $set: { status: 'active' } });","handlingStrategy":"validation","validationCode":"function isValidUpdate(doc) {\n  return Array.isArray(doc) || (doc != null && typeof doc === 'object' &&\n    Object.keys(doc).some(k => k.startsWith('$')));\n}\nif (!isValidUpdate(update)) throw new Error('updateOne needs $ operators or a pipeline');\nawait collection.updateOne(filter, update);","typeGuard":"function isAtomicUpdate(doc): doc is Record<string, unknown> {\n  return Array.isArray(doc) || (doc != null && typeof doc === 'object' &&\n    Object.keys(doc).some(k => k.startsWith('$')));\n}","tryCatchPattern":null,"preventionTips":["Use updateOne only with operator/pipeline updates; use replaceOne for full docs.","Centralize update construction in helpers that always wrap fields in $set.","Add tests covering empty/dynamic update paths."],"tags":["update","validation","argument-error"],"analyzedSha":"3366c21a6311e02f1be91da982f9b93d3cce99a0","analyzedAt":"2026-08-04T13:40:15.335Z","schemaVersion":2}