mongodb/node-mongodb-native · error · MongoInvalidArgumentError

Update document requires atomic operators

Error message

Update document requires atomic operators

What it means

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.

Solutions

  1. Wrap the fields you want to change in an atomic operator, most commonly $set: updateOne({ $set: { status: 'active' } }).
  2. If you actually intend to replace the whole document, use replaceOne(...) instead of updateOne(...).
  3. If you want aggregation-style updates, pass an array pipeline: updateOne([{ $set: { total: { $sum: ['$a','$b'] } } }]) (pipelines are detected as operator-bearing).
  4. Double-check any dynamic builder that constructs the update object to ensure the $-prefixed key survives the spread/merge.

Example fix

// before
bulk.find({ _id: 1 }).updateOne({ status: 'active' });

// after
bulk.find({ _id: 1 }).updateOne({ $set: { status: 'active' } });
Defensive patterns

Strategy: validation

Validate before calling

import { hasAtomicOperators } from 'mongodb'; // internal helper, mirror locally if not exported

function isValidUpdateOne(doc: unknown): boolean {
  return (
    doc != null &&
    typeof doc === 'object' &&
    (Array.isArray(doc)
      ? doc.every(d => hasAtomicOperators(d))
      : hasAtomicOperators(doc))
  );
}

if (!isValidUpdateOne(updateDoc)) {
  throw new Error('updateOne body must use atomic operators like $set');
}

Type guard

function isUpdateBody(doc: unknown): doc is Record<string, unknown> | Record<string, unknown>[] {
  if (doc == null || typeof doc !== 'object') return false;
  if (Array.isArray(doc)) return doc.every(d => isUpdateBody(d));
  return Object.keys(doc as Record<string, unknown>).some(k => k.startsWith('$'));
}

Try / catch

try {
  bulk.find(filter).updateOne(updateDoc);
} catch (e) {
  if (e instanceof MongoInvalidArgumentError && /atomic operators/.test(e.message)) {
    updateDoc = { $set: updateDoc };
    bulk.find(filter).updateOne(updateDoc);
  } else throw e;
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of mongodb/node-mongodb-native@dce7939f86 (2026-08-11). Data as JSON: /api/errors/b9f0852e51f2fb25. Report an issue: GitHub.

Appendix: source

Thrown at src/bulk/common.ts:736

    this.bulkOperation = bulkOperation;
  }

  /** Add a multiple update operation to the bulk operation */
  update(updateDocument: Document | Document[]): BulkOperationBase {
    const currentOp = buildCurrentOp(this.bulkOperation);
    return this.bulkOperation.addToOperationsList(
      BatchType.UPDATE,
      makeUpdateStatement(currentOp.selector, updateDocument, {
        ...currentOp,
        multi: true
      })
    );
  }

  /** Add a single update operation to the bulk operation */
  updateOne(updateDocument: Document | Document[]): BulkOperationBase {
    if (!hasAtomicOperators(updateDocument, this.bulkOperation.bsonOptions)) {
      throw new MongoInvalidArgumentError('Update document requires atomic operators');
    }

    const currentOp = buildCurrentOp(this.bulkOperation);
    return this.bulkOperation.addToOperationsList(
      BatchType.UPDATE,
      makeUpdateStatement(currentOp.selector, updateDocument, { ...currentOp, multi: false })
    );
  }

  /** Add a replace one operation to the bulk operation */
  replaceOne(replacement: Document): BulkOperationBase {
    if (hasAtomicOperators(replacement)) {
      throw new MongoInvalidArgumentError('Replacement document must not use atomic operators');
    }

    const currentOp = buildCurrentOp(this.bulkOperation);
    return this.bulkOperation.addToOperationsList(
      BatchType.UPDATE,

View on GitHub (pinned to dce7939f86)