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
- Wait for resetTimeout — the breaker then moves to HALF_OPEN and lets probe traffic through.
- Fix or verify the underlying service (the breaker opened for a reason: check failureCount/state metrics).
- Catch this error and serve a fallback/degraded response instead of propagating (see defense below).
- 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
- Always pair execute() with an open-circuit catch that serves a fallback — never propagate this error to end users.
- Don't wrap execute() in a tight retry loop: retries during OPEN are rejected instantly and add noise, not recovery.
- Expose breaker state (state, failureCount) in dashboards so 'suddenly everything fails fast' is diagnosable.
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
- Circuit breaker open for provider
- Circuit breaker is open
- MCP server " " is in cooldown (HTTP , s remaining)
- Bulkhead ' ' is full. Max concurrent: , queue
- Can only fail running or assigned tasks
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)