MongoDB: E11000 Duplicate Key Error β How to Fix It
E11000 duplicate key error collection: mydb.users index: email_1 dup key: { email: "alice@example.com" }
This error means youβre trying to insert or update a document with a value that already exists in a field with a unique index. MongoDB rejects the operation at the storage engine level.
Why this happens
MongoDB enforces unique indexes strictly. When you create a unique index on a field (like email), any attempt to insert a document with a duplicate value in that field fails with error code 11000. This also applies to:
- The default
_idfield (always has a unique index) - Compound unique indexes (the combination of fields must be unique)
- Sparse unique indexes (multiple
nullvalues are allowed, but non-null values must be unique)
Common scenarios that trigger this error:
- Retrying a failed insert β The first attempt succeeded but your app didnβt get the acknowledgment (network timeout), so it retries and hits the duplicate
- Race conditions β Two requests try to create the same user simultaneously
- Data migration β Importing data that contains duplicates
- Forgotten index β A unique index exists on a field you didnβt expect
- Null values β Multiple documents with a missing field count as duplicate
nullvalues in a non-sparse unique index
Fix 1: Use upsert (insert or update)
The most common fix. If the document exists, update it. If not, insert it.
// Mongoose
await User.findOneAndUpdate(
{ email: 'alice@example.com' },
{ $set: { name: 'Alice', lastLogin: new Date() } },
{ upsert: true, new: true }
);
// Native driver
await db.collection('users').updateOne(
{ email: 'alice@example.com' },
{ $set: { name: 'Alice', lastLogin: new Date() } },
{ upsert: true }
);
When to use: Syncing data from external sources, handling form submissions where the user might already exist, any idempotent operation.
Fix 2: Catch and handle the error
Sometimes a duplicate is expected and you just want to handle it gracefully.
try {
await db.collection('users').insertOne({ email, name, createdAt: new Date() });
} catch (err) {
if (err.code === 11000) {
// Duplicate β user already exists
console.log(`User with email ${email} already exists`);
// Optionally: return the existing document
return await db.collection('users').findOne({ email });
}
throw err; // Re-throw unexpected errors
}
With Mongoose:
try {
await User.create({ email, name });
} catch (err) {
if (err.code === 11000) {
const existing = await User.findOne({ email });
return existing;
}
throw err;
}
Fix 3: Use bulkWrite with ordered: false
When inserting many documents and some might be duplicates, use unordered bulk operations. MongoDB will insert all valid documents and report errors for duplicates without stopping.
const docs = [
{ email: 'alice@example.com', name: 'Alice' },
{ email: 'bob@example.com', name: 'Bob' },
{ email: 'alice@example.com', name: 'Alice Duplicate' }, // Will fail
];
try {
const result = await db.collection('users').bulkWrite(
docs.map(doc => ({ insertOne: { document: doc } })),
{ ordered: false }
);
console.log(`Inserted: ${result.insertedCount}`);
} catch (err) {
// err.result still contains info about successful inserts
console.log(`Inserted: ${err.result.nInserted}, Errors: ${err.writeErrors.length}`);
}
Key: ordered: false means MongoDB continues processing remaining documents even when one fails. With ordered: true (default), it stops at the first error.
Fix 4: Remove the unwanted unique index
If the unique index was created by mistake or is no longer needed:
// List all indexes to find the problematic one
const indexes = await db.collection('users').getIndexes();
console.log(indexes);
// Drop the specific index
await db.collection('users').dropIndex('email_1');
With Mongoose, check your schema for unintended unique: true:
// β This creates a unique index
const userSchema = new Schema({
email: { type: String, unique: true },
username: { type: String, unique: true }, // Did you mean this?
});
// β
Remove unique if not needed
const userSchema = new Schema({
email: { type: String, unique: true },
username: { type: String }, // Not unique
});
Important: After removing unique: true from a Mongoose schema, the index still exists in the database. You must drop it manually or use syncIndexes():
await User.syncIndexes(); // Drops indexes not in schema, creates missing ones
Fix 5: Handle null values in unique indexes
A non-sparse unique index treats missing fields as null. If multiple documents donβt have the indexed field, they all have null β which violates uniqueness.
// β Two documents without email = two null values = duplicate key error
await db.collection('users').insertOne({ name: 'Alice' }); // email: null
await db.collection('users').insertOne({ name: 'Bob' }); // email: null β E11000!
// β
Use a sparse index β ignores documents where the field is missing
await db.collection('users').createIndex(
{ email: 1 },
{ unique: true, sparse: true }
);
// β
Or use a partial filter expression (more flexible)
await db.collection('users').createIndex(
{ email: 1 },
{ unique: true, partialFilterExpression: { email: { $exists: true } } }
);
Fix 6: Handle race conditions
When two requests try to create the same document simultaneously, one will succeed and the other will get E11000. Use a retry pattern:
async function createOrGetUser(email, name) {
try {
return await db.collection('users').insertOne({ email, name });
} catch (err) {
if (err.code === 11000) {
// Another request created it first β just fetch it
return await db.collection('users').findOne({ email });
}
throw err;
}
}
This pattern is safer than βcheck then insertβ because itβs atomic β no gap between the check and the insert where another request could sneak in.
Debugging: Find which index caused the error
The error message tells you the index name:
E11000 duplicate key error collection: mydb.users index: email_1 dup key: { email: "alice@example.com" }
collection: mydb.usersβ the collectionindex: email_1β the index name (fieldemail, ascending)dup key: { email: "..." }β the duplicate value
If the index name is unexpected, list all indexes:
db.users.getIndexes()
// Returns: [{ key: { _id: 1 }, name: '_id_' }, { key: { email: 1 }, name: 'email_1', unique: true }]
FAQ
Can I have a unique index on a field that some documents donβt have?
Yes, use a sparse index or partial filter expression. Without these, all documents missing the field are treated as having null, and only one null is allowed in a unique index.
Does E11000 happen with updateOne too?
Yes. If you update a documentβs email to a value that another document already has, youβll get E11000. The uniqueness check applies to all write operations, not just inserts.
How do I handle this in Mongoose middleware?
userSchema.post('save', function(error, doc, next) {
if (error.name === 'MongoServerError' && error.code === 11000) {
next(new Error('A user with this email already exists'));
} else {
next(error);
}
});
Does this error affect performance?
No. The unique check is done via the B-tree index lookup, which is O(log n). Itβs the same cost as any indexed query. The error itself is cheap β MongoDB just rejects the write.