- Error code
- 251
- Code name
NoSuchTransaction- Product
- MongoDB
- Versions
- MongoDB 7.0 and 8.0 (wording checked against the 8.0 source)
- Category
- Transactions & Concurrency
- Last reviewed
- 24 October 2026
On this page
What it means
Error 251 (NoSuchTransaction) means the command belongs to a transaction the server no longer has open, usually because an earlier error or the time limit already aborted it.
A multi-document transaction lives on the server under the session id and a transaction number. MongoDB's documentation states that if any operation in the transaction fails, the transaction aborts and its changes are discarded. Every later command in that transaction, including commitTransaction, then gets error 251: Transaction with { txnNumber: N } has been aborted. So 251 is very often a symptom: the real cause is the first error inside the transaction (a duplicate key, a validation failure, a write conflict) that the code caught and ignored before carrying on.
Transactions also have a lifetime limit: by default a transaction must finish in under one minute (transactionLifetimeLimitSeconds, 60 seconds), and a periodic cleanup aborts expired ones. The next command after that gets 251 as well. A third group of messages (Given transaction number ... does not match any in-progress transactions) means the session is being used with a transaction number the server does not have open, for example when one session is shared between concurrent tasks or the transaction has already been cleaned up.
MongoDB 8.0 labels NoSuchTransaction as a TransientTransactionError for operations inside a transaction; on commit or abort it does so only when there is no write concern error (error_labels.cpp). With that label the whole transaction can safely be run again.
Common causes
- An earlier statement in the transaction failed (for example E11000 or a validation error), the error was caught and ignored, and the code went on to the next statement or to commit.
- The transaction ran longer than transactionLifetimeLimitSeconds (60 seconds by default) and was aborted by the cleanup process.
- The transaction was aborted explicitly (abortTransaction) or its session ended, and later code still used it.
- One session object is shared between concurrent requests or threads, so their transaction numbers interleave.
- The transaction waited too long for a lock behind a pending DDL operation (maxTransactionLockRequestTimeoutMillis) and was aborted.
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.
Find the first error in the transaction
// In the application log, look for the first error with the same lsid / txnNumber
// before the NoSuchTransaction. In mongosh, keep the error object of every statement:
try { /* statement inside the transaction */ } catch (e) {
printjson({ code: e.code, codeName: e.codeName, labels: e.errorLabels, msg: e.message })
} The code and name of the error that aborted the transaction (often 11000, 121, 112 or 50). Fix that one; the 251 disappears with it.
Check the transaction lifetime limit
db.adminCommand({ getParameter: 1, transactionLifetimeLimitSeconds: 1 }) The limit in seconds (60 by default). If the failing transactions run close to this long, the cleanup process is what aborted them.
Look at open transactions and their age
db.getSiblingDB("admin").aggregate([
{ $currentOp: { allUsers: true, idleSessions: true } },
{ $match: { transaction: { $exists: true } } },
{ $project: { lsid: 1, active: 1,
"transaction.parameters.txnNumber": 1,
"transaction.timeOpenMicros": 1,
"transaction.timeInactiveMicros": 1 } }
]) Transactions that are open, how long they have been open and how long they have been idle. A long idle time means the application holds a transaction open while doing other work.
Count aborted transactions
db.serverStatus().transactions totalStarted, totalCommitted, totalAborted and currentOpen since start-up. On a mongos the section also has abortCause, a breakdown of aborts by cause.
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.
Stop at the first error and retry the whole transaction
Client or network sideNever swallow an error inside a transaction and carry on. Let it propagate, abort, and rerun the whole transaction when the error has the TransientTransactionError label. withTransaction does this for you; if the first error is not transient (a duplicate key, a validation failure), fix the data or the logic instead of retrying.
const session = db.getMongo().startSession()
try {
session.withTransaction(() => {
const shop = session.getDatabase("shop")
shop.orders.insertOne({ _id: 1001, customer: "Sara" }) // an error here ends the attempt
shop.stock.updateOne({ sku: "PEN-1" }, { $inc: { qty: -1 } })
})
} finally {
session.endSession()
}
Keep transactions well under the time limit
Client or network sideDo slow work (remote calls, file processing, user interaction) before or after the transaction, not inside it, and split large batch jobs into smaller transactions. By default MongoDB requires a transaction to run for less than one minute.
Use one session per unit of work
Client or network sideStart a new session (or let withTransaction use one) for each request or task, and never run two transactions on the same session at the same time. Sessions are cheap; sharing them causes transaction number mismatches.
// per request
const session = client.startSession()
try { await session.withTransaction(async () => { /* work */ }) }
finally { await session.endSession() }
Raise the lifetime limit only for a known long transaction
Changes configurationIf a transaction genuinely needs more than 60 seconds and cannot be split, the limit can be raised on every mongod (every shard member in a sharded cluster).
Caution: Longer transactions hold uncommitted changes and cache longer, which MongoDB documents as a source of WiredTiger cache pressure and of more write conflicts. It applies to every transaction on the server and does not persist across restarts unless also set in the configuration file. Prefer shortening the transaction.
db.adminCommand({ setParameter: 1, transactionLifetimeLimitSeconds: 120 })
Examples
Carrying on after a duplicate key inside a transaction
Not reproduced here: no MongoDB server is installed for this site. The wording comes from MongoDB 8.0's transaction code (transaction_participant.cpp, _checkIsCommandValidWithTxnState); the txnNumber depends on how many transactions the session has run and is illustrative. Which statements abort a transaction is described in MongoDB's production considerations for transactions.
const s = db.getMongo().startSession()
const orders = s.getDatabase("shop").orders
s.startTransaction()
orders.insertOne({ _id: 10 }) // succeeds
try { orders.insertOne({ _id: 1 }) } catch (e) { print("ignored: " + e.code) } // 11000, _id 1 already exists
orders.insertOne({ _id: 2 })
MongoServerError: Transaction with { txnNumber: 1 } has been aborted.
Committing after the transaction expired
Not reproduced here: no MongoDB server is installed for this site. MongoDB documents that transactions exceeding transactionLifetimeLimitSeconds are aborted by a periodic cleanup process; the message is the 8.0 source wording for a command sent to an aborted transaction, with an illustrative txnNumber.
s.startTransaction()
s.getDatabase("shop").orders.insertOne({ _id: 3 })
sleep(90 * 1000) // longer than transactionLifetimeLimitSeconds (60)
s.commitTransaction()
MongoServerError: Transaction with { txnNumber: 2 } has been aborted.
How to prevent it
Run every transaction through withTransaction or one shared helper that stops at the first error, aborts, and retries only on TransientTransactionError. Log the first error with its code, not just the final NoSuchTransaction.
Keep transactions short, never perform remote calls inside them, and use a separate session per request or task.
Common questions
What causes "Transaction with { txnNumber } has been aborted" in MongoDB?
Something already aborted the transaction: usually an earlier statement failed (and the error was ignored), or the transaction ran past the 60 second lifetime limit. Find that first error; the NoSuchTransaction is only the consequence.
Can I retry after NoSuchTransaction?
Yes, but retry the whole transaction from startTransaction, not just the last command. MongoDB labels it TransientTransactionError in most cases, and withTransaction retries it automatically.
Should I just increase transactionLifetimeLimitSeconds?
Only for a known, unavoidable long transaction. Most of the time the better fix is to move slow work out of the transaction or split it, because long transactions increase cache pressure and write conflicts.
Learn more on CodeWithSQL
Official documentation
- MongoDB error codes (251 NoSuchTransaction)
- Transactions in applications
- Production considerations for transactions
- Server parameters (transactionLifetimeLimitSeconds)
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.