Fix "E11000 duplicate key error collection" in MongoDB
What MongoDB's E11000 duplicate key error means, how to read the index and dup key in the message, and how to fix it for _id, null values and upserts.
Quick Answer
A unique index already has a document with that value. The message tells you exactly which index and which value:
MongoServerError: E11000 duplicate key error collection: shop.users index: email_1 dup key: { email: "a@x.io" }
Here, the unique index email_1 already contains "a@x.io". Either the insert is a real duplicate (update the existing document instead, usually with an upsert), or the index is stricter than you meant it to be.
Read the error message
Every E11000 message has the same three parts, and together they point straight at the problem:
| Part | Example | What it tells you |
|---|---|---|
| collection | shop.users | Database and collection, as db.collection |
| index | email_1 | The unique index that rejected the write. Run db.users.getIndexes() to see its definition |
| dup key | { email: "a@x.io" } | The value that already exists |
Then find the document that already holds the value:
db.users.find({ email: 'a@x.io' })
The error code is 11000. Drivers expose it as err.code === 11000 in Node.js and as DuplicateKeyError in PyMongo.
What causes it
| dup key looks like | Cause | Fix |
|---|---|---|
{ _id: 5 } | You set _id yourself and reused a value | Let MongoDB generate _id, or update instead of insert |
{ email: "a@x.io" } | A real duplicate, often a retried request or a double-submitted form | Upsert, or catch code 11000 and treat it as "already exists" |
{ email: null } | Two documents without the field, under a unique index | Use a partial index |
{ username: "x" } on an index you don't expect | A leftover index from an old schema or an ORM | Drop the stale index |
What we found testing this
You don't have to parse the message string. Every client we tried exposes the index and the duplicate value as structured fields:
| Client | Error class | Index fields | Duplicate value |
|---|---|---|---|
| Node.js driver 6 | MongoServerError | err.keyPattern | err.keyValue |
| Node.js, insertMany | MongoBulkWriteError | err.writeErrors[i] | err.insertedCount = rows that got in |
| PyMongo 4.19 | DuplicateKeyError | e.details['keyPattern'] | e.details['keyValue'] |
| PyMongo, insert_many | BulkWriteError | e.details['writeErrors'] | e.details['nInserted'] |
- A case-insensitive unique index hides the value. On an index created with
collation: { locale: 'en', strength: 2 }, insertingA@x.ionext toa@x.iofailed withdup key: { email: "CollationKey(0x290a7a57083945010a)" }: a binary sort key, not the email. UsekeyValuefrom the driver or look the document up with the same collation. (Without a collation,A@x.ioanda@x.iowere both accepted: unique indexes are case-sensitive by default.) sparseand$exists: truedon't fix duplicate nulls. Both allowed any number of documents without the field, but the second document with an explicitemail: nullstill failed withdup key: { email: null }, because the field exists. OnlypartialFilterExpression: { email: { $type: 'string' } }accepted repeated nulls, which is why the fix below uses$type.- How many rows got in depends on
ordered. Inserting[1, 2, 1, 3, 4]into a unique field stored 2 documents with the default ordered insert (it stopped at the first duplicate), and inserting[1, 2, 1, 3, 1]withordered: falsestored 3 and reported 2 write errors.
Cause 1: inserting something that already exists
If the intent is "create it, or update it if it's there", don't insert. Upsert:
db.users.updateOne(
{ email: 'a@x.io' }, // match on the unique field
{ $set: { name: 'Ana', lastSeen: new Date() } },
{ upsert: true }
)
When a duplicate is expected and harmless, catch it instead:
try {
await db.collection('users').insertOne({ email })
} catch (err) {
if (err.code === 11000) return { alreadyRegistered: true }
throw err
}
from pymongo.errors import DuplicateKeyError
try:
db.users.insert_one({"email": email})
except DuplicateKeyError:
pass # already registered
Two upserts racing on the same new value can still raise E11000, because both see "no match" and both try to insert. Retry the upsert once on code 11000 and the second attempt will find the document.
Cause 2: dup key { field: null }
A unique index treats a missing field as null, so it allows exactly one document without the field. The second one fails:
db.users.insertMany([{ name: 'no email 1' }, { name: 'no email 2' }])
// E11000 duplicate key error collection: shop.users index: email_1 dup key: { email: null }
If the field is optional, make the index cover only documents that have it, with a partial index:
db.users.dropIndex('email_1')
db.users.createIndex(
{ email: 1 },
{ unique: true, partialFilterExpression: { email: { $type: 'string' } } }
)
Now any number of documents can omit email, but two documents can't share the same address.
Cause 3: creating a unique index on data with duplicates
createIndex({ email: 1 }, { unique: true }) fails with the same E11000 if duplicates already exist, prefixed with Index build failed. Find them first:
db.users.aggregate([
{ $group: { _id: '$email', count: { $sum: 1 }, ids: { $push: '$_id' } } },
{ $match: { count: { $gt: 1 } } }
])
Decide which copy to keep, delete or merge the others, then build the index.
Cause 4: bulk inserts and stale indexes
insertMany stops at the first duplicate by default, so the documents after it are never written. Pass ordered: false to insert everything that can be inserted and get one error listing the duplicates:
db.users.insertMany(docs, { ordered: false }) // MongoBulkWriteError, but non-duplicates are saved
If the index named in the error doesn't match your current schema, it's probably left over from an earlier version of the app, or created by an ORM such as Mongoose (unique: true in a schema creates an index). List and drop it:
db.users.getIndexes()
db.users.dropIndex('username_1')
Frequently Asked Questions
What is error code 11000 in MongoDB?
It is the duplicate key error. A write tried to store a value that already exists in a unique index, including the default unique index on _id.
Why do I get E11000 with dup key null?
A unique index counts a missing field as null, so only one document can lack it. Use a partial index with partialFilterExpression to index only documents that have the field.
Does Mongoose unique: true validate uniqueness?
No. It only creates a unique index in MongoDB. The database rejects the duplicate with E11000, which Mongoose passes back as an error with code 11000.
How do I insert many documents and skip duplicates?
Use insertMany with ordered: false. Non-duplicate documents are inserted, and the error lists the ones that were rejected.