mongodb/node-mongodb-native · error · MongoRuntimeError
illegal state transition from
Error message
illegal state transition from [${target.s.state}] => [${newState}], allowed: [${legalStates}] What it means
Thrown by the state machine created via makeStateMachine() when a target object attempts to move to a state not listed as legal from its current state. This generic state machine is used internally by SDAM components (topology, server, monitor) to govern lifecycle transitions like 'connecting' -> 'connected'. Raised as MongoRuntimeError with the current state, attempted state, and allowed states.
Solutions
- Ensure client.close() is called once and awaited; avoid concurrent close() calls on the same client.
- Do not reuse a MongoClient after close(); construct a fresh instance instead.
- If the error persists under correct usage, upgrade the driver and file a bug with the full stack trace.
Example fix
// before
await client.close();
await client.close(); // may race state machine
// after
let closed = false;
if (!closed) { await client.close(); closed = true; } Defensive patterns
Strategy: try-catch
Try / catch
let isClosed = false;
async function safeClose() {
if (isClosed) return;
isClosed = true;
try { await client.close(); } catch (e) {
if (e instanceof MongoRuntimeError && /illegal state transition/.test(e.message)) return;
isClosed = false; throw e;
}
} Prevention
- Call client.close() exactly once and guard it with an idempotency flag.
- Do not invoke internal SDAM methods directly; use the public MongoClient API.
- Avoid racing concurrent connect()/close() cycles on the same client instance.
When it happens
Trigger: Internal SDAM code attempting a disallowed transition (e.g. closing an already-closed topology, or a monitor transitioning from a terminal state). Not triggered by normal user API calls; indicates either a driver bug or interference with internal lifecycle (e.g. calling client.close() concurrently multiple times).
Common situations: Calling MongoClient.close() multiple times concurrently, or mixing close() with active operations in a way that races the SDAM state machine. Sometimes seen with rapid connect/disconnect cycles or with custom wrappers that invoke internal methods.
Related errors
- Argument "setName" is required if connected to a replica set
- Attempted illegal state transition from
- ConnectionPool.clear() called in load balanced mode with no…
- Limit must be less than the number of items
- Server is closed
AI-assisted analysis of mongodb/node-mongodb-native@dce7939f86 (2026-08-11).
Data as JSON: /api/errors/91ead02f90d628e5.
Report an issue: GitHub.
Appendix: source
Thrown at src/utils.ts:426
s: { state: string };
emit(event: 'stateChanged', state: string, newState: string): void;
}
interface StateTransitionFunction {
(target: ObjectWithState, newState: string): void;
}
/** @public */
export type EventEmitterWithState = {
/** @internal */
stateChanged(previous: string, current: string): void;
};
/** @internal */
export function makeStateMachine(stateTable: StateTable): StateTransitionFunction {
return function stateTransition(target, newState) {
const legalStates = stateTable[target.s.state];
if (legalStates && legalStates.indexOf(newState) < 0) {
throw new MongoRuntimeError(
`illegal state transition from [${target.s.state}] => [${newState}], allowed: [${legalStates}]`
);
}
target.emit('stateChanged', target.s.state, newState);
target.s.state = newState;
};
}
/**
* This function returns the number of milliseconds since an arbitrary point in time.
* This function should only be used to measure time intervals.
* @internal
* */
export function processTimeMS(): number {
return Math.floor(performance.now());
}
View on GitHub (pinned to dce7939f86)