{"record":{"id":"abc8c65977cec97d","repo":"mochajs/mocha","slug":"invalid-state-transition-state-newstate","errorCode":null,"errorMessage":"invalid state transition: ${state} => ${newState}","messagePattern":"invalid state transition: (.+?) => (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"lib/nodejs/parallel-buffered-runner.cjs","lineNumber":97,"sourceCode":"/**\n * This `Runner` delegates tests runs to worker threads.  Does not execute any\n * {@link Runnable}s by itself!\n * @public\n */\nclass ParallelBufferedRunner extends Runner {\n  constructor(...args) {\n    super(...args);\n\n    let state = IDLE;\n    Object.defineProperty(this, \"_state\", {\n      get() {\n        return state;\n      },\n      set(newState) {\n        if (states[state].has(newState)) {\n          state = newState;\n        } else {\n          throw new Error(`invalid state transition: ${state} => ${newState}`);\n        }\n      },\n    });\n\n    this._workerReporter = DEFAULT_WORKER_REPORTER;\n    this._linkPartialObjects = false;\n    this._linkedObjectMap = new Map();\n\n    this.once(Runner.constants.EVENT_RUN_END, () => {\n      this._state = COMPLETE;\n    });\n  }\n\n  /**\n   * Returns a mapping function to enqueue a file in the worker pool and return results of its execution.\n   * @param {BufferedWorkerPool} pool - Worker pool\n   * @param {RunnerOptions} options - Mocha options\n   * @returns {FileRunner} Mapping function","sourceCodeStart":79,"sourceCodeEnd":115,"githubUrl":"https://github.com/mochajs/mocha/blob/6bcbee4fd9a95351cedf0fdcb14c7d696486bc5a/lib/nodejs/parallel-buffered-runner.cjs#L79-L115","documentation":"The ParallelBufferedRunner exposes a read-only `state` property backed by a setter that validates transitions against a finite state machine (`states[state]` lists allowed next states). This error is thrown when code attempts an illegal transition — for example advancing to a new state from RUN_END, or setting a state that can never follow the current one.","triggerScenarios":"Programmatically setting `runner.state` to a value not allowed from the current state (e.g. `state = 'RUNNING'` after 'RUN_END'); a third-party wrapper driving the runner's lifecycle manually; double-invoking run/discard paths so the state machine is advanced twice.","commonSituations":"Custom orchestration of parallel runners in test infrastructure; reusing a completed runner for another run; race conditions where two async paths set state concurrently; version upgrades where the state machine gained new states and old driver code now makes illegal transitions.","solutions":["Don't set `runner.state` directly; drive the lifecycle through public APIs (`run()`, `abort()`) and let Mocha manage transitions.","Create a fresh ParallelBufferedRunner instead of reusing a runner that already reached a terminal state.","Check the allowed transitions for the current state before setting (inspect the `states` map in parallel-buffered-runner.cjs).","If a wrapper sets state, log both `state` and `newState` to identify which transition is illegal and fix the ordering."],"exampleFix":"// before\nrunner.state = 'RUNNING'; // from 'RUN_END' -> illegal transition\n\n// after\nconst runner = new ParallelBufferedRunner(); // fresh instance\nrunner.run(files, { serial: false });","handlingStrategy":"try-catch","validationCode":"const ALLOWED = { RUNNABLE_START: ['RUNNABLE_END','RUNNABLE_ABORTED'] };\nif (!ALLOWED[runner.state]?.includes(newState)) {\n  throw new Error(`Illegal transition from ${runner.state} to ${newState}; create a new runner instead`);\n}","typeGuard":"const canTransition = (runner, newState) =>\n  typeof runner.state === 'string' && runner.state !== 'RUN_END' && newState === 'RUNNING';","tryCatchPattern":"try {\n  runner.state = newState;\n} catch (err) {\n  if (err.message.startsWith('invalid state transition')) {\n    console.error(`${err.message} — create a fresh ParallelBufferedRunner instead of mutating state.`);\n    runner = new ParallelBufferedRunner();\n  } else {\n    throw err;\n  }\n}","preventionTips":["Never assign runner.state directly; use run()/abort().","Don't reuse a runner after its run has ended — instantiate a new one.","Serialize lifecycle mutations to avoid concurrent state writes.","When upgrading Mocha, re-check any code that manipulates parallel runner internals."],"tags":["parallel","state-machine","runner"],"backgroundTag":"invalid-state-transition","analyzedSha":"6bcbee4fd9a95351cedf0fdcb14c7d696486bc5a","analyzedAt":"2026-09-01T03:41:47.182Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}