mongodb/node-mongodb-native · error · MongoTransactionError

Transaction already in progress

Error message

Transaction already in progress

What it means

Thrown by ClientSession.startTransaction() when a transaction is already active on the session (state is STARTING_TRANSACTION or TRANSACTION_IN_PROGRESS). The driver enforces one active transaction per session per the sessions spec. It is a MongoTransactionError.

Source

Thrown at src/sessions.ts:394

  }

  /**
   * Starts a new transaction with the given options.
   *
   * @remarks
   * **IMPORTANT**: Running operations in parallel is not supported during a transaction. The use of `Promise.all`,
   * `Promise.allSettled`, `Promise.race`, etc to parallelize operations inside a transaction is
   * undefined behaviour.
   *
   * @param options - Options for the transaction
   */
  startTransaction(options?: TransactionOptions): void {
    if (this.snapshotEnabled) {
      throw new MongoCompatibilityError('Transactions are not supported in snapshot sessions');
    }

    if (this.inTransaction()) {
      throw new MongoTransactionError('Transaction already in progress');
    }

    if (this.isPinned && this.transaction.isCommitted) {
      this.unpin();
    }

    this.commitAttempted = false;
    // increment txnNumber
    this.incrementTransactionNumber();
    // create transaction state
    this.transaction = new Transaction({
      readConcern:
        options?.readConcern ??
        this.defaultTransactionOptions.readConcern ??
        this.clientOptions?.readConcern,
      writeConcern:
        options?.writeConcern ??
        this.defaultTransactionOptions.writeConcern ??

View on GitHub (pinned to 3366c21a63)

Solutions

  1. Commit or abort the existing transaction before starting a new one: await session.abortTransaction() then session.startTransaction().
  2. Check state first: if (!session.inTransaction()) session.startTransaction().
  3. Use withTransaction() which manages start/commit/abort lifecycle internally instead of calling startTransaction yourself.
  4. Audit helper functions to ensure only one layer owns transaction begin/commit.

Example fix

// before
session.startTransaction();
// ... later, branch re-enters
session.startTransaction(); // throws

// after
if (!session.inTransaction()) {
  session.startTransaction();
}
Defensive patterns

Strategy: validation

Validate before calling

if (!session.inTransaction()) {
  session.startTransaction();
}

Type guard

function isTransactionFree(session: ClientSession): boolean {
  return !session.inTransaction();
}

Try / catch

try {
  session.startTransaction();
} catch (e) {
  if (e instanceof MongoTransactionError && /already in progress/.test(e.message)) {
    // abort or commit the existing one first
    await session.abortTransaction();
    session.startTransaction();
  } else throw e;
}

Prevention

When it happens

Trigger: Two consecutive calls to session.startTransaction() with no intervening commitTransaction/abortTransaction; a re-entrant helper that starts a transaction while the caller already did.

Common situations: Refactoring code so a transaction is started both outside and inside a function; retry loops that restart a transaction without aborting the previous one; copy-paste of startTransaction boilerplate.

Related errors


AI-assisted analysis of mongodb/node-mongodb-native@3366c21a63 (2026-08-04). Data as JSON: /data/errors/af599726e0497d78.json. Report an issue: GitHub.