mongodb/node-mongodb-native · error · MongoTransactionError
Cannot call abortTransaction twice
Error message
Cannot call abortTransaction twice
What it means
`abortTransaction` throws MongoTransactionError when the state is already `TRANSACTION_ABORTED` — calling abort twice is rejected because the first abort already ended the transaction.
Solutions
- Own the abort in exactly one place; do not double-abort.
- Guard with `if (session.inTransaction()) await session.abortTransaction();` — once aborted, `inTransaction()` returns false.
- Use `withTransaction` to avoid manual abort altogether.
Example fix
// before
await session.abortTransaction();
await session.abortTransaction(); // throws
// after
if (session.inTransaction()) {
await session.abortTransaction();
} Defensive patterns
Strategy: validation
Validate before calling
function safeAbort(session) {
if (session.inTransaction()) return session.abortTransaction();
} Type guard
function canAbortTxn(session) {
return session.inTransaction();
} Prevention
- Abort in exactly one place per transaction attempt.
- Guard with `if (session.inTransaction())` since aborted transactions report not-in-progress.
- Use `withTransaction` to avoid manual abort bookkeeping.
When it happens
Trigger: `session.abortTransaction(); session.abortTransaction();` — typically nested error handlers where both the inner and outer catch attempt to abort.
Common situations: A user callback that aborts, then an outer wrapper that aborts again on the propagated error; multiple finally blocks; refactor that duplicated cleanup.
Related errors
- Cannot call abortTransaction after calling commitTransaction
- Cannot call commitTransaction after calling abortTransaction
- No transaction started
- Transaction already in progress
- Function provided to `withTransaction` must return a Promise
AI-assisted analysis of mongodb/node-mongodb-native@dce7939f86 (2026-08-11).
Data as JSON: /api/errors/9653dc156bfc3c7a.
Report an issue: GitHub.
Appendix: source
Thrown at src/sessions.ts:587
*
* @param options - Optional options, can be used to override `defaultTimeoutMS`.
*/
async abortTransaction(options?: { timeoutMS?: number }): Promise<void>;
/** @internal */
async abortTransaction(options?: { timeoutMS?: number; throwTimeout?: true }): Promise<void>;
async abortTransaction(options?: { timeoutMS?: number; throwTimeout?: true }): Promise<void> {
if (this.transaction.state === TxnState.NO_TRANSACTION) {
throw new MongoTransactionError('No transaction started');
}
if (this.transaction.state === TxnState.STARTING_TRANSACTION) {
// the transaction was never started, we can safely exit here
this.transaction.transition(TxnState.TRANSACTION_ABORTED);
return;
}
if (this.transaction.state === TxnState.TRANSACTION_ABORTED) {
throw new MongoTransactionError('Cannot call abortTransaction twice');
}
if (
this.transaction.state === TxnState.TRANSACTION_COMMITTED ||
this.transaction.state === TxnState.TRANSACTION_COMMITTED_EMPTY
) {
throw new MongoTransactionError(
'Cannot call abortTransaction after calling commitTransaction'
);
}
const command: {
abortTransaction: 1;
writeConcern?: WriteConcernOptions;
recoveryToken?: Document;
} = { abortTransaction: 1 };
const timeoutMS =View on GitHub (pinned to dce7939f86)