{"id":"0b5654b5e3f68fa1","repo":"mongodb/node-mongodb-native","slug":"update-document-requires-atomic-operators-0b5654","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/find_and_modify.ts","lineNumber":278,"sourceCode":"  override options: FindOneAndUpdateOptions;\n\n  private update: Document;\n  constructor(\n    collection: Collection,\n    filter: Document,\n    update: Document,\n    options: FindOneAndUpdateOptions\n  ) {\n    if (filter == null || typeof filter !== 'object') {\n      throw new MongoInvalidArgumentError('Argument \"filter\" must be an object');\n    }\n\n    if (update == null || typeof update !== 'object') {\n      throw new MongoInvalidArgumentError('Argument \"update\" must be an object');\n    }\n\n    if (!hasAtomicOperators(update, options)) {\n      throw new MongoInvalidArgumentError('Update document requires atomic operators');\n    }\n\n    super(collection, filter, options);\n    this.update = update;\n    this.options = options;\n  }\n\n  override buildCommandDocument(\n    connection: Connection,\n    session?: ClientSession\n  ): Document & FindAndModifyCmdBase {\n    const document = super.buildCommandDocument(connection, session);\n    document.update = this.update;\n    configureFindAndModifyCmdBaseUpdateOpts(document, this.options);\n\n    if (this.options.arrayFilters) {\n      document.arrayFilters = this.options.arrayFilters;\n    }","sourceCodeStart":260,"sourceCodeEnd":296,"githubUrl":"https://github.com/mongodb/node-mongodb-native/blob/3366c21a6311e02f1be91da982f9b93d3cce99a0/src/operations/find_and_modify.ts#L260-L296","documentation":"Thrown by FindOneAndUpdateOperation when the update argument passed to collection.findOneAndUpdate() contains no atomic update operators (keys beginning with '$'). MongoDB requires findOneAndUpdate to mutate via operators like $set, $inc, $push, or an aggregation pipeline; a plain replacement document is rejected client-side before any wire request. This is a MongoInvalidArgumentError, meaning the caller supplied invalid arguments.","triggerScenarios":"Calling collection.findOneAndUpdate(filter, update) where update is a plain object such as { name: 'x' } or { status: 1 } with no $-prefixed keys. Also triggered by passing null/{} as the update, or by accidentally swapping arguments so a filter/projection document lands in the update slot. Aggregation-pipeline updates (an array of stages) are accepted, but a non-array document without operators fails.","commonSituations":"Developers migrating from findOneAndReplace to findOneAndUpdate and forgetting to wrap fields in $set; building the update dynamically and an empty/no-op branch yields a bare object; copy-paste from a replace tutorial; TypeScript loose typing where update is typed as Document allowing any object.","solutions":["Wrap the fields you want to change in an atomic operator, e.g. { $set: { ... } } or { $inc: { count: 1 } }.","If you intended a full-document replacement, use collection.findOneAndReplace(filter, replacement, opts) instead.","If you need expressive/conditional updates, pass an aggregation pipeline array (e.g. [{ $set: { ... } }]) which is a valid update form.","Double-check argument order: findOneAndUpdate(filter, update, options) - the second argument must be the mutation document."],"exampleFix":"// before\nawait collection.findOneAndUpdate({ _id: 1 }, { name: 'x' });\n// after\nawait collection.findOneAndUpdate({ _id: 1 }, { $set: { name: 'x' } });","handlingStrategy":"validation","validationCode":"// before calling findOneAndUpdate\nimport { hasAtomicOperators } from 'mongodb'; // if exposed; else inline check\nfunction 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('update needs $ operators or a pipeline');\nawait collection.findOneAndUpdate(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":"try {\n  await collection.findOneAndUpdate(filter, update);\n} catch (e) {\n  if (e instanceof MongoInvalidArgumentError && /atomic operators/.test(e.message)) {\n    // wrap fields in $set and retry, or surface a clear user error\n  }\n  throw e;\n}","preventionTips":["Standardize on $set-wrapped update objects in shared write helpers.","Use TypeScript narrow types for update docs so a plain shape is flagged.","Add a unit test asserting your update builder always emits operators."],"tags":["update","validation","find-and-modify","argument-error"],"analyzedSha":"3366c21a6311e02f1be91da982f9b93d3cce99a0","analyzedAt":"2026-08-04T13:40:15.335Z","schemaVersion":2}