micro/go-micro · warning
circuit breaker open (consecutive failures: %d)
Error message
circuit breaker open (consecutive failures: %d)
What it means
The circuit breaker's Allow() denies calls while the circuit is open after reaching the consecutive-failure threshold. It only admits a probe once timeout has elapsed since the last failure (moving to half-open); until then every call returns this error. It protects the downstream target from being hammered while it is unhealthy.
Source
Thrown at gateway/mcp/circuitbreaker.go:97
}
}
// Allow checks whether a request should be allowed through.
// Returns nil if allowed, error if the circuit is open.
func (cb *circuitBreaker) Allow() error {
cb.mu.Lock()
defer cb.mu.Unlock()
switch cb.state {
case circuitClosed:
return nil
case circuitOpen:
if time.Since(cb.lastFailure) > cb.timeout {
cb.state = circuitHalfOpen
cb.halfOpenUsed = 0
return nil
}
return fmt.Errorf("circuit breaker open (consecutive failures: %d)", cb.failures)
case circuitHalfOpen:
if cb.halfOpenUsed < cb.maxHalfOpen {
cb.halfOpenUsed++
return nil
}
return fmt.Errorf("circuit breaker half-open (probe limit reached)")
}
return nil
}
// RecordSuccess records a successful call. If half-open, closes the circuit.
func (cb *circuitBreaker) RecordSuccess() {
cb.mu.Lock()
defer cb.mu.Unlock()
cb.failures = 0
cb.state = circuitClosed
}View on GitHub (pinned to 24529f1404)
Solutions
- Wait until the breaker's timeout elapses; the next Allow() transitions to half-open and admits a probe.
- Fix the underlying target failures that tripped the breaker (connectivity, endpoint health).
- Tune breaker configuration (failure threshold, timeout) to match the target's realistic recovery time.
- Treat the error as a signal to shed load / return a cached or degraded response instead of retrying immediately.
Example fix
// before
for {
if err := cb.Allow(); err != nil { continue } // hot loop against open breaker
}
// after
if err := cb.Allow(); err != nil {
time.Sleep(cbBackoff) // back off until the open window expires
continue
} Defensive patterns
Strategy: retry
Validate before calling
if cb.IsOpen() { // if exposed; otherwise track failures yourself
return ErrDegraded
} Type guard
func isBreakerOpen(err error) bool {
return err != nil && strings.Contains(err.Error(), "circuit breaker open")
} Try / catch
if err := cb.Allow(); err != nil {
if isBreakerOpen(err) {
return serveFallback() // cached/degraded response
}
return err
}
defer cb.RecordResult(callerErr == nil) Prevention
- Always pair Allow() with RecordSuccess/RecordFailure.
- Return a degraded response instead of hot-looping while open.
- Size the breaker timeout to the target's realistic recovery time.
- Add alerting on repeated breaker-open events.
When it happens
Trigger: Calling Allow() while the breaker is in the open state and time.Since(lastFailure) <= cb.timeout, i.e. the failure threshold was tripped and the cool-down window has not expired.
Common situations: An MCP target repeatedly failing (network outage, bad endpoint) trips the breaker; callers keep invoking tools during the cool-down; short timeout configured so the open window lasts longer than the caller expects.
Related errors
AI-assisted analysis of micro/go-micro@24529f1404 (2026-09-01).
Data as JSON: /api/errors/ce003363773b1c33.
Report an issue: GitHub.