mongodb/node-mongodb-native · error · MongoInvalidArgumentError

Replacement document must not contain atomic operators

Error message

Replacement document must not contain atomic operators

What it means

Thrown as a MongoInvalidArgumentError by the ReplaceOneOperation constructor when the replacement document contains atomic operators (keys beginning with '$'). replaceOne substitutes the entire matched document with the supplied replacement, so field-update operators like $set are illegal; those belong to updateOne/updateMany. The check uses hasAtomicOperators() at update.ts:234.

Solutions

  1. If you meant partial updates with operators, use collection.updateOne(filter, { $set: { a: 1 } }).
  2. If you meant a full replacement, remove all '$'-prefixed keys and supply the complete document.
  3. Add a preflight check that no replacement key starts with '$' before calling replaceOne.

Example fix

// before
await collection.replaceOne({ _id: id }, { $set: { status: 'on' } });

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

Strategy: validation

Validate before calling

function isPlainReplacement(v: unknown) {
  return typeof v === 'object' && v !== null && !Array.isArray(v)
    && !Object.keys(v).some(k => k.startsWith('$'));
}
if (!isPlainReplacement(replacement)) {
  throw new TypeError('replacement must not contain atomic operators; use updateOne for $ ops');
}
await collection.replaceOne(filter, replacement);

Type guard

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

Prevention

When it happens

Trigger: Calling collection.replaceOne(filter, { $set: { a: 1 } }) instead of collection.updateOne. Any replacement whose first key starts with '$' triggers this.

Common situations: Copy-pasting an update document into a replaceOne call; confusion between replaceOne (full document) and updateOne (operators); refactoring and forgetting to switch APIs.

Related errors


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

Appendix: source

Thrown at src/operations/update.ts:235

  upsert?: boolean;
  /** Map of parameter names and values that can be accessed using $$var (requires MongoDB 5.0). */
  let?: Document;
  /** Specifies the sort order for the documents matched by the filter. */
  sort?: Sort;
}

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

    if (hasAtomicOperators(replacement)) {
      throw new MongoInvalidArgumentError('Replacement document must not contain 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:
        Array.isArray(res.upserted) && res.upserted.length > 0 ? res.upserted[0]._id : null,

View on GitHub (pinned to dce7939f86)