mochajs/mocha · error · Error

invalid state transition: ${state} => ${newState}

Error message

invalid state transition: ${state} => ${newState}

What it means

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.

Source

Thrown at lib/nodejs/parallel-buffered-runner.cjs:97

/**
 * This `Runner` delegates tests runs to worker threads.  Does not execute any
 * {@link Runnable}s by itself!
 * @public
 */
class ParallelBufferedRunner extends Runner {
  constructor(...args) {
    super(...args);

    let state = IDLE;
    Object.defineProperty(this, "_state", {
      get() {
        return state;
      },
      set(newState) {
        if (states[state].has(newState)) {
          state = newState;
        } else {
          throw new Error(`invalid state transition: ${state} => ${newState}`);
        }
      },
    });

    this._workerReporter = DEFAULT_WORKER_REPORTER;
    this._linkPartialObjects = false;
    this._linkedObjectMap = new Map();

    this.once(Runner.constants.EVENT_RUN_END, () => {
      this._state = COMPLETE;
    });
  }

  /**
   * Returns a mapping function to enqueue a file in the worker pool and return results of its execution.
   * @param {BufferedWorkerPool} pool - Worker pool
   * @param {RunnerOptions} options - Mocha options
   * @returns {FileRunner} Mapping function

View on GitHub (pinned to 6bcbee4fd9)

Solutions

  1. Don't set `runner.state` directly; drive the lifecycle through public APIs (`run()`, `abort()`) and let Mocha manage transitions.
  2. Create a fresh ParallelBufferedRunner instead of reusing a runner that already reached a terminal state.
  3. Check the allowed transitions for the current state before setting (inspect the `states` map in parallel-buffered-runner.cjs).
  4. If a wrapper sets state, log both `state` and `newState` to identify which transition is illegal and fix the ordering.

Example fix

// before
runner.state = 'RUNNING'; // from 'RUN_END' -> illegal transition

// after
const runner = new ParallelBufferedRunner(); // fresh instance
runner.run(files, { serial: false });
Defensive patterns

Strategy: try-catch

Validate before calling

const ALLOWED = { RUNNABLE_START: ['RUNNABLE_END','RUNNABLE_ABORTED'] };
if (!ALLOWED[runner.state]?.includes(newState)) {
  throw new Error(`Illegal transition from ${runner.state} to ${newState}; create a new runner instead`);
}

Type guard

const canTransition = (runner, newState) =>
  typeof runner.state === 'string' && runner.state !== 'RUN_END' && newState === 'RUNNING';

Try / catch

try {
  runner.state = newState;
} catch (err) {
  if (err.message.startsWith('invalid state transition')) {
    console.error(`${err.message} — create a fresh ParallelBufferedRunner instead of mutating state.`);
    runner = new ParallelBufferedRunner();
  } else {
    throw err;
  }
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


AI-assisted analysis of mochajs/mocha@6bcbee4fd9 (2026-09-01). Data as JSON: /api/errors/abc8c65977cec97d. Report an issue: GitHub.