Skip to content
MongoDB

MongoDB Error 251

NoSuchTransaction: transaction has been aborted

Applies to: MongoDB 7.0 and 8.0 (wording checked against the 8.0 source)
Last technically reviewed 24 October 2026 on MongoDB 8.0 documentation and source (error_codes.yml r8.0.0); not run locally
The full message
MongoServerError: Transaction with { txnNumber: 2 } has been aborted.
Other forms of this message
The first command of the transaction failed
Transaction with { txnNumber: 2 } has been aborted because an earlier command in this transaction failed.
The transaction number is not the one the server knows for the session
Given transaction number 3 does not match any in-progress transactions. The active transaction number is 2
Error labels usually returned with it
errorLabels: [ 'TransientTransactionError' ], code: 251, codeName: 'NoSuchTransaction'
Error code
251
Code name
NoSuchTransaction
Product
MongoDB
Versions
MongoDB 7.0 and 8.0 (wording checked against the 8.0 source)
Last reviewed
24 October 2026

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 })
}
What to look for

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 })
What to look for

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 } }
])
What to look for

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
What to look for

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 side

Never 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 side

Do 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 side

Start 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 configuration

If 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 })
Expected result (documented format, not run)
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()
Expected result (documented format, not run)
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

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.

Still stuck on a MongoDB error?

Browse the full MongoDB error library, or get hands-on help from an experienced SQL professional.