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

  1. Read the wrapped error: 'circuit breaker does not allow requests' means the breaker is open — wait for the cool-down or reset it
  2. Fix the underlying transport failure shown by the wrapped error (connectivity, DNS, TLS)
  3. Check recent upstream API health; the breaker likely tripped on a real outage
  4. Tune breaker thresholds (failure count, open duration) if it trips too easily for your traffic
  5. 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

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


AI-assisted analysis of Billionmail/BillionMail@fc36c76c05 (2026-09-05). Data as JSON: /api/errors/773fcb82e1faffdf. Report an issue: GitHub.