mongodb/node-mongodb-native · error · MongoAPIError

Client bulk write update models must only contain atomic…

Error message

Client bulk write update models must only contain atomic modifiers (start with $) and must not be empty.

What it means

Thrown when an UpdateOne or UpdateMany client bulk write model has an update document that is empty or whose first key does not start with '$'. The driver enforces the cross-driver bulk write spec, which requires update models to use atomic operators only — replacement-style documents belong in ReplaceOne models.

Solutions

  1. Wrap field changes in $set/$unset/$inc/etc.: { $set: { field: value } }.
  2. If you genuinely want to replace the whole document, use a replaceOne model instead of updateOne/updateMany.
  3. Validate the update document programmatically before sending — first key must start with '$' and the document must not be empty.

Example fix

// before
await client.bulkWrite([{
  updateOne: { namespace: 'db.coll', filter: { _id }, update: { status: 'done' } }
}]);

// after
await client.bulkWrite([{
  updateOne: { namespace: 'db.coll', filter: { _id }, update: { $set: { status: 'done' } } }
}]);
Defensive patterns

Strategy: type-guard

Validate before calling

function isValidUpdate(doc) {
  if (!doc || typeof doc !== 'object' || Object.keys(doc).length === 0) return false;
  return Object.keys(doc).every(k => k.startsWith('$'));
}

Type guard

const isAtomicUpdate = (v: unknown): v is Record<string, object> =>
  v != null && typeof v === 'object' && Object.keys(v as object).length > 0 &&
  Object.keys(v as object).every(k => k.startsWith('$'));

Prevention

When it happens

Trigger: Passing updateOne: { namespace, filter, update: { field: value } } (no $set) or update: {} to client.bulkWrite. Also triggered by update documents whose first key is a non-$ field due to a typo like 'set' instead of '$set'.

Common situations: Migrating from collection.updateMany(filter, { field: value }) legacy code paths; copy-paste from replaceOne; building update documents dynamically and forgetting the operator wrapper.

Related errors


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

Appendix: source

Thrown at src/operations/client_bulk_write/command_builder.ts:373

 * @param model - The update many model.
 * @param index - The namespace index.
 * @returns the operation.
 */
export const buildUpdateManyOperation = (
  model: ClientUpdateManyModel<Document>,
  index: number,
  options: BSONSerializeOptions
): ClientUpdateOperation => {
  return createUpdateOperation(model, index, true, options);
};

/**
 * Validate the update document.
 * @param update - The update document.
 */
function validateUpdate(update: Document, options: BSONSerializeOptions) {
  if (!hasAtomicOperators(update, options)) {
    throw new MongoAPIError(
      'Client bulk write update models must only contain atomic modifiers (start with $) and must not be empty.'
    );
  }
}

/**
 * Creates a delete operation based on the parameters.
 */
function createUpdateOperation(
  model: ClientUpdateOneModel<Document> | ClientUpdateManyModel<Document>,
  index: number,
  multi: boolean,
  options: BSONSerializeOptions
): ClientUpdateOperation {
  // Update documents provided in UpdateOne and UpdateMany write models are
  // required only to contain atomic modifiers (i.e. keys that start with "$").
  // Drivers MUST throw an error if an update document is empty or if the
  // document's first key does not start with "$".

View on GitHub (pinned to dce7939f86)