What it means
An update document can use several operators ($set, $inc, $push, $setOnInsert…), but each
field may be changed by only one of them, and a field and a path inside it can’t both be changed.
Otherwise the result would depend on which operator ran first, so MongoDB refuses the whole update
with code 40, ConflictingUpdateOperators. Nothing is written.
The message names both paths: Updating the path 'dims.h' would create a conflict at 'dims'
means one operator changes dims.h while another changes dims itself.
Common causes
- An upsert with the same field in
$setand$setOnInsert, usually to give a default on insert:$set: { qty: 1 }, $setOnInsert: { qty: 0 }. - Two operators on one array:
$pushand$pull(or$addToSetand$pull) on the same field. - A parent and a child:
$set: { meta: {} }with$unset: { "meta.seen": "" }, or$set: { dims: { w: 1 }, "dims.h": 2 }. - The same field twice through different operators:
$setand$inconqty. - An array element and the array:
$set: { "items.0": "z" }with$push: { items: "y" }.
How to fix it
Put each field in one operator
For an upsert, a field in $set is also written on insert, so it doesn’t need $setOnInsert.
Keep in $setOnInsert only the fields that must not change on later updates:
db.carts.updateOne(
{ _id: 2 },
{ $set: { qty: 1 }, $setOnInsert: { createdAt: new Date() } },
{ upsert: true }
)
Use a pipeline update for “keep it, or default it”
When the new value depends on the current one (increment, but start from 0 on insert), an update pipeline can read the field and decide. It runs in one step:
db.carts.updateOne(
{ _id: 3 },
[{ $set: {
qty: { $add: [{ $ifNull: ["$qty", 0] }, 1] },
createdAt: { $ifNull: ["$createdAt", "$$NOW"] }
} }],
{ upsert: true }
)
Split operations on the same array
Run them as separate updates. bulkWrite sends them together and applies them in order:
db.carts.bulkWrite([
{ updateOne: { filter: { _id: 1 }, update: { $pull: { items: "a" } } } },
{ updateOne: { filter: { _id: 1 }, update: { $push: { items: "c" } } } }
])
Each update is atomic on its own; the pair isn’t, so another write can land between them. If that
matters, use a pipeline update ($filter and $concatArrays on the array) or a transaction.
Set the parent or the children, not both
Either replace the whole object ($set: { dims: { w: 1, h: 2 } }) or set each path
($set: { "dims.w": 1, "dims.h": 2 }).
In Mongoose, check what it sends
Mongoose adds operators of its own: with upsert, it puts schema defaults in $setOnInsert
(setDefaultsOnInsert), and with timestamps it sets updatedAt and createdAt. Turn on
mongoose.set("debug", true) to see the final update, and look for a field in two operators.
Reproduce it
MongoDB 8.0.32, mongosh 2.12.0, in a scratch database. An upsert that matches nothing:
db.items.updateOne(
{ sku: "B-2" },
{ $set: { qty: 1 }, $setOnInsert: { qty: 0 } },
{ upsert: true }
)
MongoServerError: Updating the path 'qty' would create a conflict at 'qty'
The error’s code was 40. On a cart { _id: 1, items: ["a", "b"], meta: { seen: 1 } }, the other
forms:
MongoServerError: Updating the path 'items' would create a conflict at 'items'
MongoServerError: Updating the path 'meta.seen' would create a conflict at 'meta'
MongoServerError: Updating the path 'dims.h' would create a conflict at 'dims'
The first came from $push and $pull on items (and also from $set on items.0 with $push),
the second from $set: { meta: {} } with $unset of meta.seen, the third from
$set: { dims: { w: 1 }, "dims.h": 2 }. $set and $inc on qty named qty.
The fixes above all worked: the upsert with qty only in $set inserted { _id: 2, createdAt: …, qty: 1 }; the pipeline upsert inserted qty: 1 and, run again, made it 2 and kept createdAt; the
bulkWrite left items: ["b", "c"].
With Mongoose 8.23.0 and mongoose.set("debug", …), an upsert sent the schema’s defaults in
$setOnInsert ({ plan: "free", "settings.theme": "light" }) beside our $set; it succeeded here
because the fields didn’t overlap.
In Inlet
Inlet’s document editor updates only the fields you edited. Queries use mongosh syntax, so you can
run the corrected updateOne, with upsert: true or a pipeline, and check the document in the
inspector afterwards.