Billionmail/BillionMail · error
circuit breaker [%s]: %w
Error message
circuit breaker [%s]: %w
What it means
Do executes the HTTP request through a circuit breaker (c.cb.Execute). When the breaker itself returns an error — the call failed and was counted, or the breaker is open and short-circuits the call — it is wrapped as 'circuit breaker [<name>]: %w'. Inspect the wrapped error to distinguish 'breaker open' from an underlying transport failure.
Source
Thrown at core/internal/service/video_gen/apiclient.go:49
return counts.ConsecutiveFailures >= 5
},
}),
}
}
// Do executes an HTTP request with rate limiting and circuit breaking.
// Blocks until rate limiter allows, then executes through circuit breaker.
func (c *RateLimitedClient) Do(req *http.Request) (*http.Response, error) {
ctx := req.Context()
if err := c.limiter.Wait(ctx); err != nil {
return nil, fmt.Errorf("rate limiter: %w", err)
}
resp, err := c.cb.Execute(func() (*http.Response, error) {
return c.client.Do(req)
})
if err != nil {
return nil, fmt.Errorf("circuit breaker [%s]: %w", c.cb.Name(), err)
}
return resp, nil
}
View on GitHub (pinned to fc36c76c05)
Solutions
- Read the wrapped error: 'circuit breaker does not allow requests' means the breaker is open — wait for the cool-down or reset it
- Fix the underlying transport failure shown by the wrapped error (connectivity, DNS, TLS)
- Check recent upstream API health; the breaker likely tripped on a real outage
- Tune breaker thresholds (failure count, open duration) if it trips too easily for your traffic
- Retry after the breaker's cool-down window rather than hammering the open breaker
Example fix
// before
resp, err := client.Do(req)
if err != nil { return nil, err } // treated as immediate fatal
// after
resp, err := client.Do(req)
var breakerErr *sonyflake.CircuitOpenError // or check strings.Contains(err.Error(), "open")
if err != nil {
if isCircuitOpen(err) { return retryAfterCooldown(req) }
return nil, err
} Defensive patterns
Strategy: try-catch
Validate before calling
// check breaker state before sending if the API exposes it
if cb.State() == circuit.Open {
return errors.New("skipping call: circuit breaker open, will retry after cooldown")
} Try / catch
resp, err := client.Do(req)
if err != nil {
if strings.Contains(err.Error(), "circuit breaker") && strings.Contains(err.Error(), "open") {
time.Sleep(cbCooldown) // wait for the breaker to half-open, then retry
return retry(req)
}
// otherwise it's the underlying transport error — log and surface it
return fmt.Errorf("upstream call failed: %w", err)
} Prevention
- Distinguish breaker-open errors from transport errors via errors.Unwrap
- Set breaker thresholds proportional to your traffic (avoid tripping on one blip)
- Add upstream health checks so the breaker can half-open and recover promptly
- Use exponential backoff with jitter when retrying against an open breaker
When it happens
Trigger: c.cb.Execute returns an error: the underlying HTTP request failed (network error, connection refused, TLS failure), or the circuit breaker is in the open state and rejects the call outright without attempting the request.
Common situations: Repeated upstream API failures tripped the breaker and now all requests fail fast with 'circuit breaker open'; upstream API outage; DNS/network problems in the deployment; too-sensitive breaker thresholds tripping on a transient blip.
Related errors
- base URL not configured
- base URL accessibility test failed: %w
- download file failed:
- download lipsync video: %w
- cartesia clone API call: %w
AI-assisted analysis of Billionmail/BillionMail@fc36c76c05 (2026-09-05).
Data as JSON: /api/errors/773fcb82e1faffdf.
Report an issue: GitHub.