mongodb/node-mongodb-native · error · MongoInvalidArgumentError

Update document requires atomic operators

Error message

Update document requires atomic operators

What it means

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.

Source

Thrown at src/operations/find_and_modify.ts:278

  override options: FindOneAndUpdateOptions;

  private update: Document;
  constructor(
    collection: Collection,
    filter: Document,
    update: Document,
    options: FindOneAndUpdateOptions
  ) {
    if (filter == null || typeof filter !== 'object') {
      throw new MongoInvalidArgumentError('Argument "filter" must be an object');
    }

    if (update == null || typeof update !== 'object') {
      throw new MongoInvalidArgumentError('Argument "update" must be an object');
    }

    if (!hasAtomicOperators(update, options)) {
      throw new MongoInvalidArgumentError('Update document requires atomic operators');
    }

    super(collection, filter, options);
    this.update = update;
    this.options = options;
  }

  override buildCommandDocument(
    connection: Connection,
    session?: ClientSession
  ): Document & FindAndModifyCmdBase {
    const document = super.buildCommandDocument(connection, session);
    document.update = this.update;
    configureFindAndModifyCmdBaseUpdateOpts(document, this.options);

    if (this.options.arrayFilters) {
      document.arrayFilters = this.options.arrayFilters;
    }

View on GitHub (pinned to 3366c21a63)

Solutions

  1. Wrap the fields you want to change in an atomic operator, e.g. { $set: { ... } } or { $inc: { count: 1 } }.
  2. If you intended a full-document replacement, use collection.findOneAndReplace(filter, replacement, opts) instead.
  3. If you need expressive/conditional updates, pass an aggregation pipeline array (e.g. [{ $set: { ... } }]) which is a valid update form.
  4. Double-check argument order: findOneAndUpdate(filter, update, options) - the second argument must be the mutation document.

Example fix

// before
await collection.findOneAndUpdate({ _id: 1 }, { name: 'x' });
// after
await collection.findOneAndUpdate({ _id: 1 }, { $set: { name: 'x' } });
Defensive patterns

Strategy: validation

Validate before calling

// before calling findOneAndUpdate
import { hasAtomicOperators } from 'mongodb'; // if exposed; else inline check
function isValidUpdate(doc) {
  return Array.isArray(doc) || (doc != null && typeof doc === 'object' &&
    Object.keys(doc).some(k => k.startsWith('$')));
}
if (!isValidUpdate(update)) throw new Error('update needs $ operators or a pipeline');
await collection.findOneAndUpdate(filter, update);

Type guard

function isAtomicUpdate(doc): doc is Record<string, unknown> {
  return Array.isArray(doc) || (doc != null && typeof doc === 'object' &&
    Object.keys(doc).some(k => k.startsWith('$')));
}

Try / catch

try {
  await collection.findOneAndUpdate(filter, update);
} catch (e) {
  if (e instanceof MongoInvalidArgumentError && /atomic operators/.test(e.message)) {
    // wrap fields in $set and retry, or surface a clear user error
  }
  throw e;
}

Prevention

When it happens

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

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

Related errors


AI-assisted analysis of mongodb/node-mongodb-native@3366c21a63 (2026-08-04). Data as JSON: /data/errors/0b5654b5e3f68fa1.json. Report an issue: GitHub.