{"record":{"id":"91ead02f90d628e5","repo":"mongodb/node-mongodb-native","slug":"illegal-state-transition-from-target-s-state","errorCode":null,"errorMessage":"illegal state transition from [${target.s.state}] => [${newState}], allowed: [${legalStates}]","messagePattern":"illegal state transition from \\[(.+?)\\] => \\[(.+?)\\], allowed: \\[(.+?)\\]","errorType":"exception","errorClass":"MongoRuntimeError","httpStatus":null,"severity":"error","filePath":"src/utils.ts","lineNumber":426,"sourceCode":"  s: { state: string };\n  emit(event: 'stateChanged', state: string, newState: string): void;\n}\ninterface StateTransitionFunction {\n  (target: ObjectWithState, newState: string): void;\n}\n\n/** @public */\nexport type EventEmitterWithState = {\n  /** @internal */\n  stateChanged(previous: string, current: string): void;\n};\n\n/** @internal */\nexport function makeStateMachine(stateTable: StateTable): StateTransitionFunction {\n  return function stateTransition(target, newState) {\n    const legalStates = stateTable[target.s.state];\n    if (legalStates && legalStates.indexOf(newState) < 0) {\n      throw new MongoRuntimeError(\n        `illegal state transition from [${target.s.state}] => [${newState}], allowed: [${legalStates}]`\n      );\n    }\n\n    target.emit('stateChanged', target.s.state, newState);\n    target.s.state = newState;\n  };\n}\n\n/**\n * This function returns the number of milliseconds since an arbitrary point in time.\n * This function should only be used to measure time intervals.\n * @internal\n * */\nexport function processTimeMS(): number {\n  return Math.floor(performance.now());\n}\n","sourceCodeStart":408,"sourceCodeEnd":444,"githubUrl":"https://github.com/mongodb/node-mongodb-native/blob/dce7939f86fb283e167ad709955abedb7bf23124/src/utils.ts#L408-L444","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","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."],"exampleFix":"// before\nawait client.close();\nawait client.close(); // may race state machine\n\n// after\nlet closed = false;\nif (!closed) { await client.close(); closed = true; }","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"let isClosed = false;\nasync function safeClose() {\n  if (isClosed) return;\n  isClosed = true;\n  try { await client.close(); } catch (e) {\n    if (e instanceof MongoRuntimeError && /illegal state transition/.test(e.message)) return;\n    isClosed = false; throw e;\n  }\n}","preventionTips":["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."],"tags":["state-machine","internal","sdam","lifecycle"],"backgroundTag":null,"analyzedSha":"dce7939f86fb283e167ad709955abedb7bf23124","analyzedAt":"2026-08-11T04:54:53.215Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}