mongodb/node-mongodb-native · error · MongoInvalidArgumentError

Update document requires atomic operators

Error message

Update document requires atomic operators

What it means

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.

Source

Thrown at src/operations/update.ts:146

      command.comment = options.comment;
    }

    return command;
  }
}

/** @internal */
export class UpdateOneOperation extends UpdateOperation {
  constructor(
    ns: MongoDBCollectionNamespace,
    filter: Document,
    update: Document,
    options: UpdateOptions
  ) {
    super(ns, [makeUpdateStatement(filter, update, { ...options, multi: false })], options);

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

  override handleOk(
    response: InstanceType<typeof this.SERVER_COMMAND_RESPONSE_TYPE>
  ): UpdateResult {
    const res = super.handleOk(response);

    // @ts-expect-error Explain typing is broken
    if (this.explain != null) return res;

    if (res.code) throw new MongoServerError(res);
    if (res.writeErrors) throw new MongoServerError(res.writeErrors[0]);

    return {
      acknowledged: this.writeConcern?.w !== 0,
      modifiedCount: res.nModified ?? res.n,
      upsertedId:

View on GitHub (pinned to 3366c21a63)

Solutions

  1. Wrap changes in $set / $inc / $push etc.: { $set: { field: value } }.
  2. If you meant whole-document replacement, use collection.replaceOne(filter, replacement).
  3. For conditional/expressive updates pass an aggregation pipeline array.
  4. Verify the call signature: updateOne(filter, update, options).

Example fix

// before
await collection.updateOne({ _id: 1 }, { status: 'active' });
// after
await collection.updateOne({ _id: 1 }, { $set: { status: 'active' } });
Defensive patterns

Strategy: validation

Validate before calling

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('updateOne needs $ operators or a pipeline');
await collection.updateOne(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('$')));
}

Prevention

When it happens

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

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

Related errors


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