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

  1. Ensure client.close() is called once and awaited; avoid concurrent close() calls on the same client.
  2. Do not reuse a MongoClient after close(); construct a fresh instance instead.
  3. 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

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


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)