ruvnet/ruflo · error · Error

Bulkhead '${this.options.name}' is full. Max concurrent: ${t

Error message

Bulkhead '${this.options.name}' is full. Max concurrent: ${this.options.maxConcurrent}, queue: ${this.options.maxQueue}

What it means

Bulkhead is a resilience4j-style concurrency limiter. execute() runs fn immediately while active < maxConcurrent, otherwise enqueues while the queue holds fewer than maxQueue entries. When both execution slots and queue slots are exhausted it increments the rejected counter, fires options.onRejected('full'), and throws — deliberate fail-fast overload protection rather than unbounded queuing.

Source

Thrown at v3/@claude-flow/shared/src/resilience/bulkhead.ts:110

  constructor(options: BulkheadOptions) {
    super();
    this.options = { ...DEFAULT_OPTIONS, ...options };
  }

  /**
   * Execute a function within the bulkhead
   */
  async execute<T>(fn: () => Promise<T>): Promise<T> {
    // If there's room for execution, run immediately
    if (this.active < this.options.maxConcurrent) {
      return this.runNow(fn);
    }

    // Check if queue is full
    if (this.queue.length >= this.options.maxQueue) {
      this.rejected++;
      this.options.onRejected?.('full');
      throw new Error(`Bulkhead '${this.options.name}' is full. Max concurrent: ${this.options.maxConcurrent}, queue: ${this.options.maxQueue}`);
    }

    // Add to queue
    return this.addToQueue(fn);
  }

  /**
   * Get current statistics
   */
  getStats(): BulkheadStats {
    return {
      active: this.active,
      queued: this.queue.length,
      maxConcurrent: this.options.maxConcurrent,
      maxQueue: this.options.maxQueue,
      completed: this.completed,
      rejected: this.rejected,
      timedOut: this.timedOut,

View on GitHub (pinned to fa13ee4ad6)

Solutions

  1. Raise maxConcurrent and/or maxQueue in the Bulkhead options to cover peak concurrency.
  2. Register options.onRejected to shed load gracefully (log, 429, drop, backpressure) instead of relying on the throw.
  3. Check getStats() before submitting and defer when active and queue are near their maxima.
  4. Attach timeouts/retries to the wrapped fn so execution slots are released promptly even when downstream hangs.

Example fix

// before
const bulkhead = new Bulkhead({ name: 'api', maxConcurrent: 5, maxQueue: 10 });
const r = await bulkhead.execute(() => callApi()); // throws at 5 active + 10 queued

// after
const bulkhead = new Bulkhead({
  name: 'api',
  maxConcurrent: 20,
  maxQueue: 100,
  onRejected: (reason) => metrics.increment('bulkhead.rejected', { reason }),
});
Defensive patterns

Strategy: fallback

Validate before calling

const s = bulkhead.getStats();
// active and queue occupancy vs your configured maxima
if (s.active >= opts.maxConcurrent && s.queue >= opts.maxQueue) {
  // at capacity: defer, shed load, or throttle before calling execute
}

Try / catch

try {
  result = await bulkhead.execute(fn);
} catch (e) {
  if (e instanceof Error && e.message.includes('is full. Max concurrent:')) {
    result = await degradeOrEnqueueElsewhere(); // serve a fallback instead of crashing
  } else {
    throw e;
  }
}

Prevention

When it happens

Trigger: Issuing more than maxConcurrent + maxQueue concurrent execute() calls; slow wrapped functions (downstream HTTP/DB calls) holding all execution slots while new submissions keep arriving; a burst of fan-out work (e.g. Promise.all over a large array) funneled through one small bulkhead.

Common situations: maxConcurrent/maxQueue sized for average rather than peak load; a degraded downstream dependency so slots never free; load tests or traffic spikes revealing the cap; missing onRejected handler so rejections only surface as exceptions.

Related errors


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