mongodb/node-mongodb-native · error · MongoAPIError
Client bulk write update models must only contain atomic…
Error message
Client bulk write update models must only contain atomic modifiers (start with $) and must not be empty.
What it means
Thrown when an UpdateOne or UpdateMany client bulk write model has an update document that is empty or whose first key does not start with '$'. The driver enforces the cross-driver bulk write spec, which requires update models to use atomic operators only — replacement-style documents belong in ReplaceOne models.
Solutions
- Wrap field changes in $set/$unset/$inc/etc.: { $set: { field: value } }.
- If you genuinely want to replace the whole document, use a replaceOne model instead of updateOne/updateMany.
- Validate the update document programmatically before sending — first key must start with '$' and the document must not be empty.
Example fix
// before
await client.bulkWrite([{
updateOne: { namespace: 'db.coll', filter: { _id }, update: { status: 'done' } }
}]);
// after
await client.bulkWrite([{
updateOne: { namespace: 'db.coll', filter: { _id }, update: { $set: { status: 'done' } } }
}]); Defensive patterns
Strategy: type-guard
Validate before calling
function isValidUpdate(doc) {
if (!doc || typeof doc !== 'object' || Object.keys(doc).length === 0) return false;
return Object.keys(doc).every(k => k.startsWith('$'));
} Type guard
const isAtomicUpdate = (v: unknown): v is Record<string, object> =>
v != null && typeof v === 'object' && Object.keys(v as object).length > 0 &&
Object.keys(v as object).every(k => k.startsWith('$')); Prevention
- Always wrap field changes in $set / $inc / $unset.
- Use replaceOne when you want to overwrite the whole document.
When it happens
Trigger: Passing updateOne: { namespace, filter, update: { field: value } } (no $set) or update: {} to client.bulkWrite. Also triggered by update documents whose first key is a non-$ field due to a typo like 'set' instead of '$set'.
Common situations: Migrating from collection.updateMany(filter, { field: value }) legacy code paths; copy-paste from replaceOne; building update documents dynamically and forgetting the operator wrapper.
Related errors
- Argument "operations" must be an array of documents
- Client bulk write replace models must not contain atomic…
- No client bulk write models were provided.
- Update document requires atomic operators
- Argument "docs" must be an array of documents
AI-assisted analysis of mongodb/node-mongodb-native@dce7939f86 (2026-08-11).
Data as JSON: /api/errors/c7b4d995bb4e67ea.
Report an issue: GitHub.
Appendix: source
Thrown at src/operations/client_bulk_write/command_builder.ts:373
* @param model - The update many model.
* @param index - The namespace index.
* @returns the operation.
*/
export const buildUpdateManyOperation = (
model: ClientUpdateManyModel<Document>,
index: number,
options: BSONSerializeOptions
): ClientUpdateOperation => {
return createUpdateOperation(model, index, true, options);
};
/**
* Validate the update document.
* @param update - The update document.
*/
function validateUpdate(update: Document, options: BSONSerializeOptions) {
if (!hasAtomicOperators(update, options)) {
throw new MongoAPIError(
'Client bulk write update models must only contain atomic modifiers (start with $) and must not be empty.'
);
}
}
/**
* Creates a delete operation based on the parameters.
*/
function createUpdateOperation(
model: ClientUpdateOneModel<Document> | ClientUpdateManyModel<Document>,
index: number,
multi: boolean,
options: BSONSerializeOptions
): ClientUpdateOperation {
// Update documents provided in UpdateOne and UpdateMany write models are
// required only to contain atomic modifiers (i.e. keys that start with "$").
// Drivers MUST throw an error if an update document is empty or if the
// document's first key does not start with "$".View on GitHub (pinned to dce7939f86)