mongodb/node-mongodb-native · error · MongoTransactionError

Cannot call commitTransaction after calling abortTransaction

Error message

Cannot call commitTransaction after calling abortTransaction

What it means

`commitTransaction` throws MongoTransactionError when the transaction state is `TRANSACTION_ABORTED` — once a transaction has been aborted it cannot be committed. The abort is terminal for that transaction attempt.

Solutions

  1. Use a single owner for the transaction lifecycle; let one place commit or abort.
  2. Switch to `withTransaction` which never reaches this state.
  3. After aborting, decide explicitly whether to start a new transaction rather than committing the aborted one.

Example fix

// before
try {
  session.startTransaction();
  await collection.insertOne(doc, { session });
} catch (err) {
  await session.abortTransaction();
} finally {
  await session.commitTransaction(); // throws
}

// after
try {
  session.startTransaction();
  await collection.insertOne(doc, { session });
  await session.commitTransaction();
} catch (err) {
  await session.abortTransaction();
  throw err;
}
Defensive patterns

Strategy: validation

Validate before calling

// Decide abort-or-commit in exactly one place; never commit after abort.
function cleanup(session, { shouldCommit }) {
  if (shouldCommit && session.inTransaction()) return session.commitTransaction();
  if (session.inTransaction()) return session.abortTransaction();
}

Prevention

When it happens

Trigger: `session.startTransaction(); ... await session.abortTransaction(); await session.commitTransaction();` — usually an error-handling block that aborts then a finally block that also tries to commit.

Common situations: Two try/catch layers each doing transaction cleanup; a generic cleanup that always commits in finally clashing with an inner catch that aborts; refactor that introduced a duplicate commit path.

Related errors


AI-assisted analysis of mongodb/node-mongodb-native@dce7939f86 (2026-08-11). Data as JSON: /api/errors/5c6c77358667c37c. Report an issue: GitHub.

Appendix: source

Thrown at src/sessions.ts:444

   *
   * @param options - Optional options, can be used to override `defaultTimeoutMS`.
   */
  async commitTransaction(options?: { timeoutMS?: number }): Promise<void> {
    if (this.transaction.state === TxnState.NO_TRANSACTION) {
      throw new MongoTransactionError('No transaction started');
    }

    if (
      this.transaction.state === TxnState.STARTING_TRANSACTION ||
      this.transaction.state === TxnState.TRANSACTION_COMMITTED_EMPTY
    ) {
      // the transaction was never started, we can safely exit here
      this.transaction.transition(TxnState.TRANSACTION_COMMITTED_EMPTY);
      return;
    }

    if (this.transaction.state === TxnState.TRANSACTION_ABORTED) {
      throw new MongoTransactionError(
        'Cannot call commitTransaction after calling abortTransaction'
      );
    }

    const command: {
      commitTransaction: 1;
      writeConcern?: WriteConcernSettings;
      recoveryToken?: Document;
      maxTimeMS?: number;
    } = { commitTransaction: 1 };

    const timeoutMS =
      typeof options?.timeoutMS === 'number'
        ? options.timeoutMS
        : typeof this.timeoutMS === 'number'
          ? this.timeoutMS
          : null;

View on GitHub (pinned to dce7939f86)