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
- Move $ operators into an updateOne or updateMany model instead of replaceOne.
- Ensure the replacement field is a non-empty plain document with no $-prefixed keys.
- 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
- Reserve replaceOne for full-document overwrites with no $ operators.
- Switch to updateOne if you find $set creeping into the replacement.
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
- Argument "operations" must be an array of documents
- Client bulk write update models must only contain atomic…
- No client bulk write models were provided.
- Raw operations are not allowed
- Replacement document must not use atomic operators
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)