mongodb/node-mongodb-native · error · MongoRuntimeError

Attempted illegal state transition from [${this.state}] to [

Error message

Attempted illegal state transition from [${this.state}] to [${nextState}]

What it means

Thrown by the @internal Transaction.transition() when the requested state change is not allowed by the transaction state machine. The state machine only permits specific transitions (e.g. NO_TRANSACTION -> STARTING_TRANSACTION, TRANSACTION_IN_PROGRESS -> COMMITTED/ABORTED); any other pairing throws MongoRuntimeError with both the current and target states in the message.

Source

Thrown at src/transactions.ts:163

  /**
   * Transition the transaction in the state machine
   * @param nextState - The new state to transition to
   */
  transition(nextState: TxnState): void {
    const nextStates = stateMachine[this.state];
    if (nextStates && nextStates.includes(nextState)) {
      this.state = nextState;
      if (
        this.state === TxnState.NO_TRANSACTION ||
        this.state === TxnState.STARTING_TRANSACTION ||
        this.state === TxnState.TRANSACTION_ABORTED
      ) {
        this.unpinServer();
      }
      return;
    }

    throw new MongoRuntimeError(
      `Attempted illegal state transition from [${this.state}] to [${nextState}]`
    );
  }

  pinServer(server: Server): void {
    if (this.isActive) {
      this._pinnedServer = server;
    }
  }

  unpinServer(): void {
    this._pinnedServer = undefined;
  }
}

export function isTransactionCommand(command: Document): boolean {
  return !!(command.commitTransaction || command.abortTransaction);
}

View on GitHub (pinned to 3366c21a63)

Solutions

  1. Ensure no two async operations share the same ClientSession concurrently - serialize all work on a session.
  2. Use withTransaction() rather than manual start/commit/abort to keep transitions in-spec.
  3. If the call sequence is correct and you still hit this, report a driver bug on the NODE Jira project with the full sequence and states shown in the message.

Example fix

// before - concurrent session use produces illegal transitions
await Promise.all([
  coll.insertOne(a, { session }),
  coll.insertOne(b, { session }) // same session, parallel -> illegal state
]);

// after - serialize operations on the session
await coll.insertOne(a, { session });
await coll.insertOne(b, { session });
Defensive patterns

Strategy: try-catch

Validate before calling

// serialize all work on a session - never share it across concurrent ops
for (const doc of docs) {
  await coll.insertOne(doc, { session }); // sequential, not Promise.all
}

Try / catch

try {
  session.startTransaction();
  await runOps(session);
  await session.commitTransaction();
} catch (e) {
  if (e instanceof MongoRuntimeError && /illegal state transition/.test(e.message)) {
    // reset by aborting if possible and starting fresh, or report a driver bug
    if (session.inTransaction()) await session.abortTransaction();
  } else throw e;
}

Prevention

When it happens

Trigger: Driver-internal logic attempting a forbidden transition, e.g. trying to go from TRANSACTION_COMMITTED back to TRANSACTION_IN_PROGRESS, or from NO_TRANSACTION directly to TRANSACTION_IN_PROGRESS. Usually surfaces when the public API is called in an unexpected order that the per-method guards did not catch first.

Common situations: Driver bugs in transaction lifecycle handling; rare interleavings with retries/CSOT; concurrent use of one session from multiple async tasks (which the driver explicitly does not support).

Related errors


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