Circuit Breaker

The budget circuit breaker monitors the rate of cost accumulation across a sliding window. When the spend rate exceeds the configured threshold, it trips open and blocks all traffic for the policy until it auto-resets or is manually reset.

This is distinct from the cost-based budget algorithm: the budget limiter enforces a total period spend cap, while the circuit breaker triggers on velocity — a sudden spike in spending even if the total budget hasn’t been reached.

Configuration

{
  "spec": {
    "circuit_breaker": {
      "enabled": true,
      "spend_rate_threshold_per_minute": 500,
      "action": "reject",
      "auto_reset_after_minutes": 5,
      "alert": true
    },
    "rules": [ ... ]
  }
}

Fields

Field Type Required Default Description
enabled boolean yes Must be true to activate.
spend_rate_threshold_per_minute number yes if enabled Positive. Trips when the rolling per-minute spend rate reaches this value.
action string no "reject" Currently only "reject" is supported.
auto_reset_after_minutes number no 0 If > 0, the breaker automatically closes after this many minutes. 0 = never auto-resets.
alert boolean no false If true, emits a structured circuit_breaker_tripped alert event when tripping.

How it works

The circuit breaker uses a weighted two-window rolling rate over 60-second windows.

State

Two keys are maintained per composite limit key:

cb_state:{limit_key}           →  "open:{opened_at_epoch}"  (when tripped)
cb_rate:{limit_key}:{window}   →  accumulated_cost (TTL = 120s)

The {window} is floor(now / 60) * 60 — the start of the current 60-second bucket.

Rate calculation

On each request (before the cost for that request is applied):

weight        = elapsed_in_current_window / 60
spend_rate    = previous_window_total × (1 − weight) + current_window_total

The weighted sum gives a smooth estimate that avoids sharp step changes at window boundaries.

Trip condition

If spend_rate >= spend_rate_threshold_per_minute:

  1. The cb_state key is set to "open:{current_time}"
  2. The request is rejected with reason: circuit_breaker_open
  3. If alert: true, an event is queued for delivery to the SaaS control plane

Auto-reset

If auto_reset_after_minutes > 0, the circuit breaker transitions to closed when:

current_time - opened_at >= auto_reset_after_minutes × 60

On the next request after the reset interval, the cb_state key is deleted and the breaker behaves as closed.

Manual reset

To reset a tripped circuit breaker immediately, increment bundle_version and push a new bundle. The new bundle initialises fresh state.

Response when tripped

HTTP 429 Too Many Requests
X-Fairvisor-Reason: circuit_breaker_open
Retry-After: 1

Policy/rule attribution is available in debug session headers (X-Fairvisor-Debug-*).

Cost source for the circuit breaker

The circuit breaker uses the first rule’s first limit_key descriptor as the partition key, and the cost from the first rule’s algorithm config. This means the circuit breaker and the first rule share the same key dimension.

Example: protect against runaway LLM agents

{
  "circuit_breaker": {
    "enabled": true,
    "spend_rate_threshold_per_minute": 10000,
    "auto_reset_after_minutes": 10,
    "alert": true
  }
}

If an agentic system suddenly starts burning 10,000 tokens per minute (e.g., stuck in a loop that escaped loop detection), the breaker trips and blocks all traffic on the policy for 10 minutes. The alert: true flag sends an event to the SaaS dashboard for operator notification.

Interaction with other limiters

Circuit breaker evaluation happens after loop detection and before per-rule limiter evaluation. If the breaker is open, no limiters are invoked.