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 FindOneAndReplaceOperation constructor when the replacement document contains keys beginning with '$' (atomic operators like $set, $inc). A replace operation substitutes the entire document, so field-update operators are illegal in the replacement; those belong to findOneAndUpdate. The check uses hasAtomicOperators() at find_and_modify.ts:239.

Solutions

  1. If you meant partial updates with operators, use collection.findOneAndUpdate(filter, { $set: { a: 1 } }) instead.
  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 findOneAndReplace.

Example fix

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

// after
await collection.findOneAndUpdate({ _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 findOneAndUpdate for $ ops');
}
await collection.findOneAndReplace(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.findOneAndReplace(filter, { $set: { a: 1 } }) instead of collection.findOneAndUpdate. Any replacement whose first key starts with '$' triggers this.

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

Related errors


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

Appendix: source

Thrown at src/operations/find_and_modify.ts:240

/** @internal */
export class FindOneAndReplaceOperation extends FindAndModifyOperation {
  private replacement: Document;
  constructor(
    collection: Collection,
    filter: Document,
    replacement: Document,
    options: FindOneAndReplaceOptions
  ) {
    if (filter == null || typeof filter !== 'object') {
      throw new MongoInvalidArgumentError('Argument "filter" must be an object');
    }

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

    if (hasAtomicOperators(replacement)) {
      throw new MongoInvalidArgumentError('Replacement document must not contain atomic operators');
    }

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

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

/** @internal */

View on GitHub (pinned to dce7939f86)