Automattic/mongoose · error · Error
Invalid atomic update value for ${op}. Expected an object, r
Error message
Invalid atomic update value for ${op}. Expected an object, received ${typeof val} What it means
castUpdate requires each update operator's value to be a non-null object (and not a Buffer) — that object maps field paths to values. After $set-sugar normalization, if an operator in mongodbUpdateOperators ($set, $inc, $push, $pull, $unset, $currentDate, $min, $max, $mul, $rename, $setOnInsert, $addToSet, $pop, $pullAll, $bit) has a non-object value, Mongoose throws a plain Error telling you the received typeof. Documents are auto-converted with toObject() first, so only genuine non-objects reach the check.
Source
Thrown at lib/helpers/query/castUpdate.js:153
// cast each value
i = ops.length;
while (i--) {
const op = ops[i];
val = ret[op];
hasDollarKey = hasDollarKey || op.startsWith('$');
if (val?.$__) {
val = val.toObject(internalToObjectOptions);
ret[op] = val;
}
if (val &&
typeof val === 'object' &&
!Buffer.isBuffer(val) &&
mongodbUpdateOperators.has(op)) {
walkUpdatePath(schema, val, op, options, context, filter);
} else {
const msg = 'Invalid atomic update value for ' + op + '. '
+ 'Expected an object, received ' + typeof val;
throw new Error(msg);
}
if (op.startsWith('$') && utils.isEmptyObject(val)) {
delete ret[op];
}
}
if (utils.hasOwnKeys(ret) === false &&
options.upsert &&
utils.hasOwnKeys(filter)) {
// Trick the driver into allowing empty upserts to work around
// https://github.com/mongodb/node-mongodb-native/pull/2490
// Shallow clone to avoid passing defaults in re: gh-13962
return { $setOnInsert: { ...filter } };
}
return ret;
};
View on GitHub (pinned to 49cdab0136)
Solutions
- Give every operator an object of path→value pairs: { $inc: { count: 1 } }, { $unset: { field: '' } }
- JSON.parse incoming update payloads and assert the shape before passing them to updateOne/updateMany/findOneAndUpdate
- For replacement-style updates, omit operators entirely and pass the plain doc to Model.replaceOne / findOneAndReplace
Example fix
// before
User.updateOne({ _id }, { $inc: 1 });
// after
User.updateOne({ _id }, { $inc: { count: 1 } }); Defensive patterns
Strategy: validation
Validate before calling
const UPDATE_OPS = new Set(['$set','$unset','$inc','$dec','$mul','$min','$max','$rename','$setOnInsert','$push','$pull','$addToSet','$pop','$pullAll','$currentDate','$bit']);
function assertUpdateShape(update) {
for (const [op, v] of Object.entries(update)) {
if (UPDATE_OPS.has(op) && (v == null || typeof v !== 'object' || Buffer.isBuffer(v))) {
throw new Error(`${op} value must be an object of { field: value }`);
}
}
} Type guard
type UpdateOp = Record<string, Record<string, unknown>>;
function isShapedUpdate(u: unknown): u is UpdateOp {
return typeof u === 'object' && u !== null && !Array.isArray(u) &&
Object.entries(u).every(([k, v]) => !k.startsWith('$') || (typeof v === 'object' && v !== null && !Buffer.isBuffer(v)));
} Try / catch
try { await Model.updateOne(f, u); } catch (err) { if (/Invalid atomic update value/.test(err.message)) { /* the message names the op and typeof received — wrap the value in { field: value } */ } throw err; } Prevention
- Never pass scalars directly under an update operator
- Parse and shape-check update payloads from APIs before use
- Remember $unset classic form is { field: '' }, not a string
When it happens
Trigger: Model.updateOne(filter, { $inc: 1 }); { $set: 'name' }; { $push: Buffer.from(...) }; { $unset: '' }; { $set: null } — any operator whose value is a string, number, boolean, null/undefined, or Buffer.
Common situations: Flattening { $set: { count: 1 } } into { $set: 1 } when refactoring; passing a serialized update from an API body without parsing; reusing classic $unset string syntax ({ $unset: 'field' }) which is invalid — $unset needs { field: '' }.
Related errors
- Query filter must be an object, got an array ${util.inspect(
- Got null array filter in ${arrayFilters}
- Model.findOneAndUpdate() no longer accepts a callback
- Cannot pass an array to query updates unless the `updatePipe
- Arguments must be aggregate pipeline operators
AI-assisted analysis of Automattic/mongoose@49cdab0136 (2026-08-21).
Data as JSON: /api/errors/a78b5df0b4e04ea7.
Report an issue: GitHub.