mongodb/node-mongodb-native · error · MongoAPIError

Client bulk write replace models must not contain atomic…

Error message

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

What it means

Thrown when a ReplaceOne client bulk write model's replacement document is empty or contains keys that start with '$'. Per the bulk write spec, replaceOne must take a full replacement document (no atomic operators); if you need $ operators, use updateOne or updateMany instead.

Solutions

  1. Move $ operators into an updateOne or updateMany model instead of replaceOne.
  2. Ensure the replacement field is a non-empty plain document with no $-prefixed keys.
  3. Add a pre-flight type guard that rejects replacement documents whose first key starts with '$'.

Example fix

// before
await client.bulkWrite([{
  replaceOne: { namespace: 'db.coll', filter: { _id }, replacement: { $set: { a: 1 } } }
}]);

// after (use updateOne for $ operators)
await client.bulkWrite([{
  updateOne: { namespace: 'db.coll', filter: { _id }, update: { $set: { a: 1 } } }
}]);
// or a true replacement
await client.bulkWrite([{
  replaceOne: { namespace: 'db.coll', filter: { _id }, replacement: { a: 1, b: 2 } }
}]);
Defensive patterns

Strategy: type-guard

Validate before calling

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

Type guard

const isReplacementDoc = (v: unknown): v is Record<string, unknown> =>
  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 replaceOne: { namespace, filter, replacement: { $set: {...} } } or replacement: {} to client.bulkWrite. Also triggered by accidentally putting $set/$inc inside the replacement field.

Common situations: Copy-pasting an update document into a replaceOne model; refactoring between update and replace paths without adjusting the document shape; building replacement dynamically and ending up empty.

Related errors


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

Appendix: source

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

  updateMods: WithoutId<Document>;
  hint?: Hint;
  upsert?: boolean;
  collation?: CollationOptions;
  sort?: SortForCmd;
}

/**
 * Build the replace one operation.
 * @param model - The replace one model.
 * @param index - The namespace index.
 * @returns the operation.
 */
export const buildReplaceOneOperation = (
  model: ClientReplaceOneModel<Document>,
  index: number
): ClientReplaceOneOperation => {
  if (hasAtomicOperators(model.replacement)) {
    throw new MongoAPIError(
      'Client bulk write replace models must not contain atomic modifiers (start with $) and must not be empty.'
    );
  }

  const document: ClientReplaceOneOperation = {
    update: index,
    multi: false,
    filter: model.filter,
    updateMods: model.replacement
  };
  if (model.hint) {
    document.hint = model.hint;
  }
  if (model.upsert) {
    document.upsert = model.upsert;
  }
  if (model.collation) {
    document.collation = model.collation;

View on GitHub (pinned to dce7939f86)