- Error code
- 112
- Code name
WriteConflict- Product
- MongoDB
- Versions
- MongoDB 5.0 to 8.0 (wording changed in 7.0; checked against the r6.0.0, r7.0.0 and r8.0.0 source)
- Category
- Transactions & Concurrency
- Last reviewed
- 24 October 2026
On this page
What it means
Error 112 (WriteConflict) means two operations tried to change the same document at the same time and this one lost; inside a transaction the whole transaction is aborted and must be retried.
MongoDB's storage engine lets only one uncommitted change to a document exist at a time. When a second operation tries to modify a document that another operation has already changed and not yet committed, the second one gets a write conflict. For a single write outside a transaction the server normally retries this internally (MongoDB 8.0's writeConflictRetry helper), so applications rarely see it. Inside a multi-document transaction it cannot retry for you: the transaction is aborted and the error is returned. MongoDB documents the case directly: if a write outside the transaction changes a document that the transaction later tries to modify, the transaction aborts because of a write conflict.
Because it is a transaction failure with no lasting effect, the server attaches the TransientTransactionError label (error_labels.cpp in the 8.0 source lists WriteConflict as transient). That label is the signal to rerun the whole transaction from the start, not just the failing statement. The callback API (withTransaction) in mongosh and the drivers does this automatically; the core API (startTransaction and commitTransaction) leaves it to you. Since MongoDB 7.0 the message has the form Caused by :: <context> :: Please retry..., where the context names the internal place the conflict was detected.
MongoDB also documents a cache case: an uncommitted transaction that puts too much pressure on the WiredTiger cache is aborted with a write conflict error. From 6.2 a transaction that can never fit returns TransactionTooLargeForCache instead; earlier versions returned TemporarilyUnavailable or WriteConflict.
Common causes
- Two transactions update the same document (a counter, a stock level, an account balance) at the same moment.
- A transaction reads a document and later updates it after a write outside the transaction has already changed it.
- A long transaction keeps documents modified but uncommitted for a long time, so other writers keep colliding with it.
- One "hot" document is updated by almost every request (a global counter or a status document).
- A large or long transaction puts pressure on the WiredTiger cache and is aborted with a write conflict.
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.
List open transactions and how long they have been open
db.getSiblingDB("admin").aggregate([
{ $currentOp: { allUsers: true, idleSessions: true } },
{ $match: { transaction: { $exists: true } } },
{ $project: { lsid: 1, active: 1, ns: 1,
"transaction.parameters.txnNumber": 1,
"transaction.timeOpenMicros": 1,
"transaction.timeInactiveMicros": 1 } }
]) One document per open transaction. Large timeOpenMicros values, or transactions that sit inactive for long periods, show the long-running transactions that other writers collide with.
Count write conflicts and aborted transactions
const s = db.serverStatus()
printjson({
writeConflicts: s.metrics.operation.writeConflicts,
transactions: s.transactions
}) metrics.operation.writeConflicts (operations that met a write conflict) and the transactions section (totalStarted, totalAborted, totalCommitted, currentOpen). Compare two readings a minute apart to see the rate; a high abort ratio means retries are frequent.
Find the operations that hit conflicts (when the profiler is on)
db.system.profile.find(
{ writeConflicts: { $gt: 0 } },
{ op: 1, ns: 1, command: 1, writeConflicts: 1, millis: 1, ts: 1 }
).sort({ ts: -1 }).limit(20) Profiled operations with their writeConflicts count, which shows the collection and the filter of the hot document.
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.
Use the callback API so the transaction is retried
Client or network sidewithTransaction reruns the callback when an error carries TransientTransactionError and retries the commit on UnknownTransactionCommitResult. Keep the callback free of side effects outside the database (emails, HTTP calls), because it may run more than once.
const session = db.getMongo().startSession()
try {
session.withTransaction(() => {
const shop = session.getDatabase("shop")
shop.stock.updateOne({ sku: "PEN-1", qty: { $gte: 1 } }, { $inc: { qty: -1 } })
shop.orders.insertOne({ sku: "PEN-1", qty: 1, at: new Date() })
})
} finally {
session.endSession()
}
Retry on the error label with the core API
Client or network sideIf you manage transactions yourself, abort and rerun the whole transaction when the error has the TransientTransactionError label. Check the label, not the code, and cap the number of attempts with a short backoff.
function runTxn(session, work, attempts = 5) {
for (let i = 1; ; i++) {
session.startTransaction()
try {
work(session)
session.commitTransaction()
return
} catch (e) {
try { session.abortTransaction() } catch (ignored) {}
const transient = (e.errorLabels || []).includes("TransientTransactionError")
if (!transient || i >= attempts) throw e
sleep(50 * i)
}
}
}
Make transactions short and touch hot documents last
Client or network sideDo reads, validation and calculations before starting the transaction, keep only the writes inside it, and update the most contended document as late as possible so it is held uncommitted for the shortest time.
Avoid a single hot document
Changes schemaWhen one document is updated by every request, spread the writes: keep several counter documents and sum them on read, or record events and aggregate them later. A single atomic update outside a transaction ($inc on one document) also avoids the transaction abort, because the server retries that write itself.
// one of N counter shards, chosen at random per write
db.counters.updateOne(
{ _id: { name: "page_views", shard: Math.floor(Math.random() * 8) } },
{ $inc: { n: 1 } },
{ upsert: true }
)
Examples
Two transactions decrement the same stock document
Not reproduced here: no MongoDB server is installed for this site. The wording is built by MongoDB 8.0's source (exception_util.h, throwWriteConflictException, with the context string from plan_executor_impl.cpp, used when an operation cannot yield, as inside a transaction). The exact context text depends on where the conflict is detected, and the server also returns the TransientTransactionError label.
// session A
sA.startTransaction()
sA.getDatabase("shop").stock.updateOne({ sku: "PEN-1" }, { $inc: { qty: -1 } })
// session B, before A commits
sB.startTransaction()
sB.getDatabase("shop").stock.updateOne({ sku: "PEN-1" }, { $inc: { qty: -1 } })
MongoServerError: Caused by :: Write conflict during plan execution and yielding is disabled. :: Please retry your operation or multi-document transaction.
The same conflict on MongoDB 6.0
Not reproduced here: no MongoDB server is installed for this site. This is the fixed text of WriteConflictException in the MongoDB 5.0 and 6.0 source (write_conflict_exception.cpp at r5.0.0 and r6.0.0); MongoDB 7.0 and 8.0 use the "Caused by ::" form instead.
// same two sessions as above, on a MongoDB 6.0 replica set
MongoServerError: WriteConflict error: this operation conflicted with another operation. Please retry your operation or multi-document transaction.
How to prevent it
Write every transaction through withTransaction (or an equivalent wrapper that retries on TransientTransactionError), keep transactions short, and design data so that concurrent requests rarely update the same document.
Track metrics.operation.writeConflicts and transactions.totalAborted over time; a rising rate is an early sign of a hot document or of transactions that stay open too long.
Common questions
How do I fix WriteConflict error 112 in MongoDB?
Retry the whole transaction when the error carries the TransientTransactionError label (withTransaction does it for you), then reduce how often it happens: shorter transactions, fewer updates to the same hot document.
Why do I get WriteConflict only inside transactions?
For a single write outside a transaction the server retries the conflict internally until it succeeds. Inside a multi-document transaction it cannot, so it aborts the transaction and returns error 112 for the application to retry.
Is retrying a write conflict safe?
Yes for the database work: the aborted transaction left no changes. Rerun the whole transaction, not only the failed statement, and keep non-database side effects outside the retried code.
Learn more on CodeWithSQL
Official documentation
- MongoDB error codes (112 WriteConflict)
- Transactions in applications (TransientTransactionError)
- Production considerations for transactions (write conflicts)
- Session.withTransaction()
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.