ruvnet/ruflo · error

Worker ${this.id} at capacity (${maxTasks} tasks)

Error message

Worker ${this.id} at capacity (${maxTasks} tasks)

What it means

WorkerBase.executeTask refuses work once currentTaskCount reaches config.maxConcurrentTasks (default 1). The check runs before the task starts; the counter is decremented when execution finishes, so the worker only accepts work below the cap — it does not queue.

Source

Thrown at v3/@claude-flow/integration/src/worker-base.ts:408

  }

  // ===== Task Execution =====

  /**
   * Execute a task with wrapper logic
   *
   * Handles load tracking, metrics, and error handling.
   *
   * @param task - Task to execute
   * @returns Task result with metrics
   */
  async executeTask(task: Task): Promise<TaskResult> {
    this.ensureInitialized();

    // Check capacity
    const maxTasks = this.config.maxConcurrentTasks || 1;
    if (this.currentTaskCount >= maxTasks) {
      throw new Error(`Worker ${this.id} at capacity (${maxTasks} tasks)`);
    }

    this.currentTaskCount++;
    this.updateLoad();
    this.status = 'busy';
    const startTime = Date.now();

    this.emit('task-started', { workerId: this.id, taskId: task.id });

    try {
      // Execute via subclass implementation
      const output = await this.execute(task);

      const duration = Date.now() - startTime;

      // Update metrics
      this.updateMetricsSuccess(duration, output.tokensUsed);

View on GitHub (pinned to fa13ee4ad6)

Solutions

  1. Serialize per worker: await each executeTask before starting the next
  2. Raise config.maxConcurrentTasks — only if the subclass's execute() is actually safe to run concurrently
  3. Dispatch through a worker pool so busy workers are routed around instead of thrown on

Example fix

// before
await Promise.all([t1, t2].map(t => worker.executeTask(t))); // second call throws: cap is 1

// after
for (const t of [t1, t2]) {
  await worker.executeTask(t); // serialized, stays under the cap
}
// or, when execute() is concurrency-safe:
new MyWorker({ id, maxConcurrentTasks: 4 });
Defensive patterns

Strategy: validation

Validate before calling

// Check the cap before dispatching; track occupancy via task events
const max = worker.config.maxConcurrentTasks ?? 1;
const busy = inFlightCount.get(worker.id) ?? 0; // increment on 'task-started', decrement on completion
if (busy >= max) {
  await waitForWorkerIdle(worker.id); // or route to another worker
}
await worker.executeTask(task);

Try / catch

try {
  await worker.executeTask(task);
} catch (e) {
  if (e instanceof Error && /at capacity/.test(e.message)) {
    // backpressure: requeue the task or pick another worker; do not busy-retry
  }
  throw e;
}

Prevention

When it happens

Trigger: Dispatching a second concurrent task to a worker with maxConcurrentTasks unset (default 1) while the first is still running — e.g. Promise.all over the same worker instance.

Common situations: Assuming the worker queues internally (it throws instead); forgetting the default cap is 1; fanning out N tasks to one worker without raising the setting.

Related errors


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