ruvnet/ruflo · error

Pool ${this.id} at maximum capacity (${this.config.maxWorker

Error message

Pool ${this.id} at maximum capacity (${this.config.maxWorkers} workers)

What it means

WorkerPool.spawn enforces config.maxWorkers: once workers.size reaches the cap, further spawns throw unless options.replace is set. The pool never silently evicts workers to make room.

Source

Thrown at v3/@claude-flow/integration/src/worker-pool.ts:315

    this.initialized = false;

    this.emit('pool-shutdown', { poolId: this.id });
  }

  /**
   * Spawn a new worker in the pool
   *
   * @param config - Worker configuration
   * @param options - Spawn options
   * @returns Created worker
   */
  spawn(
    config: WorkerConfig | SpecializedWorkerConfig | LongRunningWorkerConfig,
    options: SpawnOptions = {}
  ): WorkerBase {
    // Check capacity
    if (this.workers.size >= this.config.maxWorkers! && !options.replace) {
      throw new Error(
        `Pool ${this.id} at maximum capacity (${this.config.maxWorkers} workers)`
      );
    }

    // Handle replacement
    if (this.workers.has(config.id)) {
      if (options.replace) {
        this.terminate(config.id);
      } else {
        throw new Error(`Worker ${config.id} already exists in pool`);
      }
    }

    // Merge with default config
    const mergedConfig = {
      ...this.config.defaultWorkerConfig,
      ...config,
    };

View on GitHub (pinned to fa13ee4ad6)

Solutions

  1. Terminate an expendable worker first (pool.terminate(id)) to free a slot, then spawn
  2. Pass { replace: true } when the spawn is intentionally swapping an existing worker
  3. Raise config.maxWorkers if the workload legitimately needs more

Example fix

// before
for (let i = 0; i < 10; i++) pool.spawn({ id: `w${i}`, type: 'coder' }); // maxWorkers=4 -> 5th call throws

// after
pool.terminate(idleWorkerId()); // free a slot before hitting the cap
pool.spawn({ id: 'w5', type: 'coder' });
Defensive patterns

Strategy: validation

Validate before calling

// Track pool occupancy via your own spawn/terminate accounting
const spawnedIds = new Set<string>();
function spawnWithHeadroom(cfg: WorkerConfig, maxWorkers: number) {
  if (spawnedIds.size >= maxWorkers) {
    pool.terminate(pickIdleWorkerId()); // free a slot first
    spawnedIds.delete(pickIdleWorkerId());
  }
  const w = pool.spawn(cfg);
  spawnedIds.add(cfg.id);
  return w;
}

Try / catch

try {
  pool.spawn(cfg);
} catch (e) {
  if (e instanceof Error && /maximum capacity/.test(e.message)) {
    // evict an idle worker and retry once, or reject the load — do not loop spawning
  }
  throw e;
}

Prevention

When it happens

Trigger: Spawning a new worker when the pool already holds maxWorkers entries and SpawnOptions.replace is not true.

Common situations: An autoscaler spawning on demand without terminating idle workers first; a fixed pool sized too small for peak load; scale-up logic that ignores the configured cap.

Related errors


AI-assisted analysis of ruvnet/ruflo@fa13ee4ad6 (2026-08-18). Data as JSON: /api/errors/b1695cd160f079d1. Report an issue: GitHub.