Skip to content
MongoDB

MongoDB Error 66

Performing an update on the path '_id' would modify the immutable field '_id'

Applies to: MongoDB 7.0 and 8.0 (wording checked against the 8.0 source)
Last technically reviewed 24 October 2026 on MongoDB 8.0 documentation and source (error_codes.yml r8.0.0); not run locally
The full message
MongoServerError: Performing an update on the path '_id' would modify the immutable field '_id'
Other forms of this message
Replacement document with a different _id (replaceOne, findOneAndReplace)
After applying the update, the (immutable) field '_id' was found to have been altered to _id: 2
Update operator that changes an existing sub-field of an embedded _id
Performing an update on the path '_id.region' would modify the immutable field '_id'
Update operator that adds a new sub-field under _id (the server prints the new element)
Updating the path '_id.region' to <new element> would modify the immutable field '_id'
Error code
66
Code name
ImmutableField
Product
MongoDB
Versions
MongoDB 7.0 and 8.0 (wording checked against the 8.0 source)
Last reviewed
24 October 2026

What it means

Error 66 (ImmutableField) means an update or replacement tried to change a field MongoDB does not allow to change, almost always _id, so the whole write was rejected.

MongoDB's document model fixes the rule: the _id field is the primary key, its value must be unique in the collection, and it is immutable. Once a document is stored, no update may give it a different _id. The update code in MongoDB 8.0 checks every path an update touches against the immutable paths and raises code 66 when one would change; the message names the path you tried to write and the immutable field it collides with.

Setting _id to the value it already has is allowed: the server compares the new value with the original and only fails when it differs. That is why the error often appears only for some documents, for example when an object loaded from the database is sent back in $set after its _id was turned from an ObjectId into a string, or when a replacement document carries a newly generated _id. The same rule applies to replaceOne: in MongoDB 8.0's replacement code the stored _id is kept when the replacement has none, and a replacement that carries a different _id is rejected.

Common causes

  • The application passes a whole object, including _id, to $set, and the _id in the object differs from the stored one (often an ObjectId converted to a string by a serialiser).
  • A replacement document in replaceOne or findOneAndReplace carries a different or newly generated _id.
  • An upsert or update tries to "rename" a document by changing its key, for example correcting a mistyped natural key used as _id.
  • An update operator such as $set, $unset or $inc targets a sub-field of an _id that is an embedded document (_id.region).
  • An ODM marks _id as changed after a merge of two objects and includes it in the generated update.

Diagnose it

Read-only These mongosh commands only read data, indexes, statistics and server state (find, aggregate without $out or $merge, getIndexes, stats, currentOp, rs.status). They do not change data, users or settings, so they are safe to run first.

Compare the stored _id with the one in your update

db.orders.find({ _id: ObjectId("6537a1f0c2b8e4a1d0f1a2b3") }, { _id: 1 })

// and the type of the stored value
db.orders.aggregate([
  { $match: { _id: ObjectId("6537a1f0c2b8e4a1d0f1a2b3") } },
  { $project: { idType: { $type: "$_id" } } }
])
What to look for

The stored _id and its BSON type (for example objectId). If the update sends the same characters as a string, or a different value, the server sees a change to _id and raises 66.

Log the exact update document the application sends

// Read-only: print the update instead of running it
const update = { $set: payload }
printjson(update)
print("_id in $set: " + ("_id" in update.$set))
What to look for

Whether _id is part of the fields being set. With ODMs, enable query logging or the driver's command monitoring to see the generated command.

Check whether _id is an embedded document

db.orders.aggregate([
  { $group: { _id: { $type: "$_id" }, docs: { $sum: 1 } } }
])
What to look for

The BSON types used for _id in the collection. An object type means updates to paths such as _id.region count as changes to _id.

How to fix it

Each fix is labelled with what it changes. Try read-only checks and the least invasive fix first, and test changes outside production.

Leave _id out of $set

Client or network side

Filter on _id and set only the other fields. In JavaScript, destructuring removes _id from the object before the update; most ODMs have an option to exclude the key from generated updates.

const { _id, ...fields } = order

db.orders.updateOne(
  { _id: _id },
  { $set: fields }
)

Keep the original _id in a replacement

Client or network side

replaceOne may omit _id (the server keeps the existing one) or repeat the same value with the same type. Do not let the application generate a new _id for a document that already exists.

db.orders.replaceOne(
  { _id: ObjectId("6537a1f0c2b8e4a1d0f1a2b3") },
  { customer: "Sara", total: 42, status: "paid" }    // no _id: the stored one is kept
)

Change the key by copying the document, then removing the old one

Changes data

When a document really needs a new _id (for example a natural key that was entered wrongly), insert a copy with the new _id and delete the original, inside a transaction so both happen or neither does. Anything that refers to the old _id must be updated as well.

Caution: This deletes the original document. Run it on one document at a time, check the copy first, and update every reference to the old key (other collections, caches, URLs) in the same change. Transactions need a replica set or sharded cluster.

const session = db.getMongo().startSession()
session.withTransaction(() => {
  const coll = session.getDatabase("shop").products
  const doc = coll.findOne({ _id: "SKU-1O1" })
  coll.insertOne({ ...doc, _id: "SKU-101" })
  coll.deleteOne({ _id: "SKU-1O1" })
})
session.endSession()

Keep a separate business key

Changes schema

If a value can change (an SKU, an email, a username), do not use it as _id. Keep the generated ObjectId as _id and store the business key in its own field with a unique index, so it can be updated normally.

db.products.createIndex({ sku: 1 }, { unique: true })

Examples

Sending the _id back as a string in $set

Not reproduced here: no MongoDB server is installed for this site. The wording is built by MongoDB 8.0's update code (modifier_node.cpp, checkImmutablePathsNotModified) with this example's path filled in. The update changes _id because a string is a different value from the stored ObjectId.

db.orders.insertOne({ _id: ObjectId("6537a1f0c2b8e4a1d0f1a2b3"), status: "new" })

// the object came back from an API, where _id became a string
const order = { _id: "6537a1f0c2b8e4a1d0f1a2b3", status: "paid" }

db.orders.updateOne(
  { _id: ObjectId(order._id) },
  { $set: order }
)
Expected result (documented format, not run)
MongoServerError: Performing an update on the path '_id' would modify the immutable field '_id'

A replacement document with a different _id

Not reproduced here: no MongoDB server is installed for this site. The wording comes from MongoDB 8.0's replacement update code (object_replace_executor.cpp); the value after "altered to" is the new element as the server prints it, shown here for this example's values.

db.items.insertOne({ _id: 1, name: "Lamp" })

db.items.replaceOne(
  { _id: 1 },
  { _id: 2, name: "Lamp (blue)" }
)
Expected result (documented format, not run)
MongoServerError: After applying the update, the (immutable) field '_id' was found to have been altered to _id: 2

How to prevent it

Never pass a whole document straight into $set: build the update from the fields that actually changed, or strip _id in one helper that every update goes through. Keep _id types consistent between the database and the API layer (convert strings back to ObjectId at the boundary).

Use generated values (ObjectId) for _id and put business keys that might change in separate fields with unique indexes.

Common questions

Can I change the _id of a MongoDB document?

No. MongoDB's documentation states that _id is immutable. To give a document a new key, insert a copy with the new _id and delete the original, ideally in a transaction, and update every reference to the old key.

Why does $set with the same _id sometimes work and sometimes fail?

The server allows setting _id to its current value and rejects only a change. The same characters as a string instead of an ObjectId, or a number of a different value, count as a change and raise error 66.

Does replaceOne need the _id in the replacement document?

No. If you omit _id the existing value is kept. If you include it, it must be the same value as the stored one, otherwise the replacement fails with error 66.

Learn more on CodeWithSQL

Official documentation

Last technically reviewed 24 October 2026; examples and diagnostics run on MongoDB 8.0 documentation and source (error_codes.yml r8.0.0); not run locally. Codes and code names are checked against MongoDB's error_codes.yml (8.0); no MongoDB server was available, so the examples were not run locally.

Still stuck on a MongoDB error?

Browse the full MongoDB error library, or get hands-on help from an experienced SQL professional.