Troubleshooting
The breaker never trips
- Failures are only counted when the guarded function throws (or
recordFailureis called). If your function catches its own errors and returns normally, the breaker sees a success. Let the error propagate, or callrecordFailureexplicitly. failureThresholdfailures must land withinwindowMs. A slow trickle of errors ages out of the rolling window before the threshold is reached — lowerfailureThresholdor increasewindowMs.
The breaker never recovers
- Recovery starts only after
openDurationMsand on the next request — a passivegetStatedoes not flip OPEN → HALF_OPEN. Make a real call (orexecute) after the cooldown. - In HALF_OPEN, a single probe failure re-opens the breaker. If the dependency is still flapping, it will keep re-opening; raise
openDurationMsto back off longer.
The breaker opens too eagerly
- Increase
failureThreshold, or shrinkwindowMsso transient spikes age out faster. - Make sure you are not sharing one key across unrelated dependencies — use a distinct
keyper dependency.
@WithCircuitBreaker has no effect
- The decorator wraps the method via a proxy and needs the plugin registered in
RedisModule.forRoot({ plugins: [new CircuitBreakerPlugin()] }). Until the plugin initializes, calls run without the breaker and a warning is logged. - The decorator works on any Injectable method — but the instance must be created by Nest (so the wrapped descriptor is used).
The breaker seems stuck in HALF_OPEN
Each permitted half-open probe consumes a slot (halfOpenInFlight). If a probe's outcome is never recorded (the process crashed mid-probe, or recording failed because the state store was momentarily unavailable — this is logged), the slot is reclaimed automatically after probeTimeoutMs (default: openDurationMs), so recovery resumes on its own.
If calls are being rejected in half-open, it usually just means a probe is still in flight (or its probeTimeoutMs has not elapsed yet). Remedies if you need to act faster:
- immediate — call
reset(key)(or the admin tooling from Recipes) to force the circuit back to CLOSED; - tune — lower
probeTimeoutMsif your probes should resolve quickly, or raisehalfOpenMaxCallsso one slow probe cannot exhaust the probe budget.
As a last backstop, all circuit keys carry a TTL (about 2 × max(windowMs, openDurationMs) + 60s) after which the state self-heals completely.
CircuitBreakerStoreError under load
- This means the state store (Redis) failed, not that the breaker opened. Choose
errorPolicy: 'fail-open'to keep serving traffic when Redis is unavailable, or'fail-closed'(default) to surface the error.
Cluster deployments
Each circuit uses three keys sharing a hash tag ({cb:key}, {cb:key}:f, {cb:key}:p), so they always resolve to the same slot. No extra configuration is required for Redis Cluster.