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
- Ensure no two async operations share the same ClientSession concurrently - serialize all work on a session.
- Use withTransaction() rather than manual start/commit/abort to keep transitions in-spec.
- 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
- Never run two operations concurrently on the same ClientSession; serialize them.
- Use withTransaction() to keep transitions spec-compliant.
- Report persistent occurrences as a driver bug with the from/to states from the message.
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
- Unexpected HostAddress ${JSON.stringify(hostAddress)}
- Unexpected null session. A cursor creating command should ha
- An unexpected error type: ${typeof error}
- Transaction already in progress
- No transaction started
AI-assisted analysis of mongodb/node-mongodb-native@3366c21a63 (2026-08-04).
Data as JSON: /data/errors/5fb465a77a22e59e.json.
Report an issue: GitHub.