mongodb/node-mongodb-native · error · MongoTransactionError

No transaction started

Error message

No transaction started

What it means

Thrown by ClientSession.commitTransaction() when the transaction state is NO_TRANSACTION, i.e. startTransaction was never called (or the prior transaction was fully cleared). The driver refuses to send a commit command with no transaction context. It is a MongoTransactionError.

Source

Thrown at src/sessions.ts:431

        this.clientOptions?.writeConcern,
      readPreference:
        options?.readPreference ??
        this.defaultTransactionOptions.readPreference ??
        this.clientOptions?.readPreference,
      maxCommitTimeMS: options?.maxCommitTimeMS ?? this.defaultTransactionOptions.maxCommitTimeMS
    });

    this.transaction.transition(TxnState.STARTING_TRANSACTION);
  }

  /**
   * Commits the currently active transaction in this session.
   *
   * @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: {

View on GitHub (pinned to 3366c21a63)

Solutions

  1. Gate commit on active state: if (session.inTransaction()) await session.commitTransaction().
  2. Use withTransaction() to let the driver own commit timing.
  3. Restructure so commitTransaction is only on the success path of a started transaction.
  4. After committing/aborting, start a new transaction before committing again.

Example fix

// before
try {
  if (needsWrite) session.startTransaction();
  await coll.insertOne(doc, { session });
} finally {
  await session.commitTransaction(); // throws when needsWrite was false
}

// after
try {
  if (needsWrite) {
    session.startTransaction();
    await coll.insertOne(doc, { session });
    await session.commitTransaction();
  }
} catch (e) {
  if (session.inTransaction()) await session.abortTransaction();
  throw e;
}
Defensive patterns

Strategy: validation

Validate before calling

if (session.inTransaction()) {
  await session.commitTransaction();
}

Type guard

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

Try / catch

try {
  await session.commitTransaction();
} catch (e) {
  if (e instanceof MongoTransactionError && /No transaction started/.test(e.message)) {
    // no-op; nothing to commit
  } else throw e;
}

Prevention

When it happens

Trigger: Calling commitTransaction() on a fresh session that never started a transaction; calling commit again after a prior commit/abort cycle reset state to NO_TRANSACTION.

Common situations: A finally block that always calls commitTransaction regardless of whether start ran; conditional start paths where the start branch was skipped but commit runs unconditionally; using the same session across sequential transactions without restarting.

Related errors


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