E11000 duplicate key error collection is MongoDB's unique index constraint doing exactly what it's designed to do — rejecting an insert or update that would create a duplicate value for a field with a unique index — and the error message itself contains the specific index and value that conflicted, which is the fastest path to understanding what actually happened.
This error means an insert or update operation attempted to write a value into a field (or combination of fields) covered by a unique index, and that value already exists in another document in the collection — MongoDB enforces uniqueness at the index level, rejecting the operation rather than allowing a silent duplicate.
Why This Error Happens
Unique indexes explicitly declare that a field's values must be distinct across all documents in a collection. This error fires precisely at the moment a write operation would violate that constraint — commonly due to a genuine logic error (trying to insert the same user twice, perhaps from a retried request that wasn't properly deduplicated), a race condition (two concurrent requests both checking for existence and both proceeding to insert before either commits), or simply expected behavior your application needs to handle gracefully (a user signing up with an already-registered email).
Reproducing and Reading the Error
A duplicate email insert against a unique index:
await db.collection("users").createIndex({ email: 1 }, { unique: true });
await db.collection("users").insertOne({ email: "user@example.com", name: "Alex" });
await db.collection("users").insertOne({ email: "user@example.com", name: "Sam" });
// MongoServerError: E11000 duplicate key error collection: mydb.users
// index: email_1 dup key: { email: "user@example.com" }
The error message directly names the index (email_1) and the specific duplicate value ("user@example.com") — this is usually enough to immediately understand which field and value caused the conflict without further investigation.
Core Concepts Behind This Error
A race condition between a "check if exists" query and a subsequent insert is a common, subtle cause — two concurrent requests can both query and find no existing document, both proceed to insert, and one of them will hit E11000 even though the check-then-insert logic looked correct in isolation; the unique index is the actual source of truth, not the preceding check.
Compound unique indexes (covering multiple fields together) produce this error when the combination is duplicated, even if individual fields aren't unique on their own — understanding whether your unique constraint is on a single field or a field combination is necessary to correctly interpret and fix a specific occurrence of this error.
Upsert operations (updateOne with upsert: true) can still hit E11000 under concurrent execution, since the upsert's own internal check-and-write isn't atomic against a concurrent operation targeting the same unique value — this surprises developers who assume upsert inherently avoids duplicate key issues.
This error is often expected, user-facing behavior, not a bug to eliminate — a duplicate email during signup, for instance, should be caught and translated into a clear "email already registered" message, not treated as an exceptional failure to prevent from ever occurring.
Fixing E11000 Duplicate Key Errors
Fix 1: Catch the specific error code and translate it into a clear, expected application-level response rather than letting it propagate as a generic failure:
try {
await db.collection("users").insertOne({ email, name });
} catch (err: any) {
if (err.code === 11000) {
throw new Error("Email already registered");
}
throw err;
}
Fix 2: Rely on the unique index as the actual source of truth rather than a preceding existence check, since the check-then-insert pattern is inherently race-prone — let the index enforce uniqueness and handle the resulting error, rather than trying to prevent the race with application-level checking alone:
// Don't rely solely on this for correctness under concurrency:
const existing = await db.collection("users").findOne({ email });
if (existing) throw new Error("Email already registered");
// still insert and handle E11000 as the actual enforcement mechanism
await db.collection("users").insertOne({ email, name });
Fix 3: Use findOneAndUpdate with upsert: true for idempotent "create or update" logic instead of separate check-then-insert steps, when the operation's intent is genuinely upsert-like:
await db.collection("users").findOneAndUpdate(
{ email },
{ $setOnInsert: { name, createdAt: new Date() } },
{ upsert: true }
);
Should Application Code Ever Prevent Duplicates Instead of Relying on the Unique Index?
The unique index should always be the actual enforcement mechanism for data integrity — application-level existence checks are useful for providing fast, early user feedback (checking availability before form submission, for example) but should never be relied upon as the sole guarantee against duplicates, since they're inherently vulnerable to race conditions between concurrent requests that a database-level constraint doesn't have.
Preventing E11000 Errors From Becoming Confusing Production Failures
Catch MongoDB's error code 11000 explicitly wherever a unique constraint violation is a plausible, expected outcome (user registration, any "must be unique" business field), translating it into a clear application-level error rather than a generic 500. Design unique indexes deliberately around your actual business uniqueness requirements (single field vs. compound), and treat the index — not a preceding application-level check — as the actual source of truth for preventing duplicates under concurrent load.
If you're hitting E11000 unexpectedly, read the error message's named index and duplicate value first — it directly identifies the conflicting field and value, which is usually enough to understand whether it's a genuine bug, a race condition, or expected user-facing behavior you need to handle gracefully.