mongodb/node-mongodb-native · error · MongoTransactionError

No transaction started

Error message

No transaction started

What it means

`commitTransaction` throws MongoTransactionError when the session's transaction state is `NO_TRANSACTION` — there is nothing to commit because `startTransaction` was never called on this session (or it was already reset).

Solutions

  1. Only call commitTransaction after a successful startTransaction and at least one operation.
  2. Track transaction state yourself or check `session.inTransaction()` before committing.
  3. Prefer `withTransaction` which handles the lifecycle automatically.
  4. Make commit/abort calls conditional on having started: wrap in `if (session.inTransaction())`.

Example fix

// before
await session.commitTransaction(); // no startTransaction was ever called

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

Strategy: validation

Validate before calling

function safeCommit(session) {
  if (!session.inTransaction() && session.transaction?.state !== 'STARTING_TRANSACTION') {
    throw new Error('no transaction in progress to commit');
  }
  return session.commitTransaction();
}

Type guard

function hasStartedTxn(session) {
  return session.inTransaction();
}

Prevention

When it happens

Trigger: `session.commitTransaction()` without a prior `startTransaction()`; reusing a session whose previous transaction already committed/aborted and was not restarted; misrouted session in a wrapper.

Common situations: Helper that commits in a finally block even when start was skipped due to an earlier error; refactoring that removed the start call but left the commit; conditional transaction logic where the condition was false.

Related errors


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

Appendix: 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 dce7939f86)