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
- If you meant partial updates with operators, use collection.findOneAndUpdate(filter, { $set: { a: 1 } }) instead.
- If you meant a full replacement, remove all '$'-prefixed keys and supply the complete document.
- 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
- Remember: replace = full document, update = $ operators.
- Run a key-prefix check on dynamic replacements before calling findOneAndReplace.
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
- Argument "replacement" must be an object
- Replacement document must not contain atomic operators
- Update document requires atomic operators
- Update document requires atomic operators
- Argument "filter" must be an object
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)