vitest-dev/vitest · error · Error

Cannot set concurrency id because there are no valid free id

Error message

Cannot set concurrency id because there are no valid free ids.

What it means

Internal assertion in Pool.getConcurrencyId: it scans the workerIds map for the first free id but finds none. workerIds is initialized in setMaxWorkers to exactly maxWorkers entries (1..maxWorkers), so having no free id while scheduling a new task means more tasks were started in parallel than maxWorkers allows — an invariant violation in the scheduler.

Source

Thrown at packages/vitest/src/node/pools/pool.ts:278

    if (customPool != null && customPool.name === task.worker) {
      return new PoolRunner(options, customPool.createPoolWorker(options))
    }

    throw new Error(`Runner ${task.worker} is not supported. Test files: ${formatFiles(task)}.`)
  }

  private getConcurrencyId() {
    let concurrencyId: number | undefined

    this.workerIds.forEach((state, id) => {
      if (state && concurrencyId == null) {
        concurrencyId = id
        this.workerIds.set(id, false)
      }
    })

    if (concurrencyId == null) {
      throw new Error('Cannot set concurrency id because there are no valid free ids.')
    }

    return concurrencyId
  }

  private freeWorkerId(id: number) {
    this.workerIds.set(id, true)
  }
}

function withResolvers() {
  let resolve = () => {}
  let reject = (_error: unknown) => {}

  const promise = new Promise<void>((res, rej) => {
    resolve = res
    reject = rej
  })

View on GitHub (pinned to d568f8ce37)

Solutions

  1. Report as a Vitest bug with reproduction; this is a scheduler invariant, not a config error.
  2. Verify maxWorkers is a positive integer (>=1) in config to rule out a 0-sizing edge case.
  3. Retry with a different pool ('threads' vs 'forks') to see if the bug is pool-specific.
  4. Avoid mixing isolate:false runner reuse if it correlates with the failure.
Defensive patterns

Strategy: try-catch

Validate before calling

// Ensure maxWorkers is a positive integer to avoid 0-sizing the id map
function validateMaxWorkers(maxWorkers) {
  if (maxWorkers != null && (!Number.isInteger(maxWorkers) || maxWorkers < 1)) {
    throw new Error(`maxWorkers must be a positive integer, got ${maxWorkers}`)
  }
}

Try / catch

try {
  await vitest.start()
} catch (e) {
  if (e.message === 'Cannot set concurrency id because there are no valid free ids.') {
    console.error('Pool scheduler invariant violated — report as a Vitest bug.')
  }
  throw e
}

Prevention

When it happens

Trigger: schedule() starts a task and calls getConcurrencyId() at packages/vitest/src/node/pools/pool.ts:267-278, but every id in workerIds is already marked used. This implies the activeTasks count exceeded maxWorkers or workerIds was not sized correctly via setMaxWorkers.

Common situations: An internal scheduling bug where activeTasks.length guard at pool.ts:68 is bypassed; maxWorkers set to 0 by a config edge case; a regression in id freeing (freeWorkerId).

Related errors


AI-assisted analysis of vitest-dev/vitest@d568f8ce37 (2026-08-03). Data as JSON: /data/errors/2c50a41ab2cc777a.json. Report an issue: GitHub.