ruvnet/ruflo · error

Circuit breaker is open. Service temporarily unavailable.

Error message

Circuit breaker is open. Service temporarily unavailable.

What it means

CircuitBreaker.execute() first calls isAllowed(); when the breaker is in OPEN state — or in HALF_OPEN and the request is randomly rejected per halfOpenRequestPercentage — it throws immediately WITHOUT invoking your function. The breaker reached OPEN because consecutive failures crossed the configured threshold within the monitoring window, so the library is fail-fasting to protect the downstream service and your latency.

Solutions

  1. Wait for resetTimeout — the breaker then moves to HALF_OPEN and lets probe traffic through.
  2. Fix or verify the underlying service (the breaker opened for a reason: check failureCount/state metrics).
  3. Catch this error and serve a fallback/degraded response instead of propagating (see defense below).
  4. Tune config: raise failureThreshold, shorten resetTimeout for faster recovery, or set halfOpenRequestPercentage to 100 so half-open always probes.

Example fix

// before
const data = await breaker.execute(() => fetchProvider(id)); // throws while open

// after
let data;
try {
  data = await breaker.execute(() => fetchProvider(id));
} catch (e) {
  if (e instanceof Error && e.message.startsWith('Circuit breaker is open')) {
    data = await cachedCopyOf(id); // degrade gracefully
  } else throw e;
}
Defensive patterns

Strategy: fallback

Validate before calling

// Pre-flight: expose breaker state so callers can skip doomed calls
function isCircuitLikelyOpen(breaker: { getState(): { state: 'closed' | 'open' | 'half-open'; failureCount: number } }): boolean {
  return breaker.getState().state === 'open';
}
if (isCircuitLikelyOpen(breaker)) return cachedOrDefault(id); // don't even attempt while OPEN

Type guard

function isCircuitOpenError(e: unknown): e is Error {
  return e instanceof Error && e.message.startsWith('Circuit breaker is open');
}

Try / catch

try {
  return await breaker.execute(() => callService(id));
} catch (e) {
  if (isCircuitOpenError(e)) {
    metrics.increment('service.degraded_fallback');
    return cachedOrDefault(id); // serve stale/default — do NOT retry immediately, that defeats the breaker
  }
  throw e; // real failures propagate and are counted by recordFailure inside execute()
}

Prevention

When it happens

Trigger: Calling execute() while the wrapped dependency (API, DB, provider) has been failing past the failure threshold; any call during the OPEN window before resetTimeout elapses; a subset of calls in HALF_OPEN probing state when halfOpenRequestPercentage < 100.

Common situations: A provider outage tripping the breaker, then every request failing instantly with this error even after the provider partially recovers; aggressive thresholds (low failureThreshold, short monitoringWindow) opening on transient blips; retry storms keeping the breaker open.

Related errors


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

Appendix: source

Thrown at v3/@claude-flow/cli/src/production/circuit-breaker.ts:116

      case 'open':
        return false;

      case 'half-open':
        // Allow a percentage of requests through
        return Math.random() < this.config.halfOpenRequestPercentage;

      default:
        return true;
    }
  }

  /**
   * Execute a function through the circuit breaker
   */
  async execute<T>(fn: () => Promise<T>): Promise<T> {
    if (!this.isAllowed()) {
      throw new Error('Circuit breaker is open. Service temporarily unavailable.');
    }

    this.totalRequests++;

    try {
      const result = await fn();
      this.recordSuccess();
      return result;
    } catch (error) {
      this.recordFailure();
      throw error;
    }
  }

  /**
   * Record a successful operation
   */
  recordSuccess(): void {

View on GitHub (pinned to fa13ee4ad6)