App-vNext/Polly · error · InvalidOperationException

This instance of 'CircuitBreakerStateProvider' is already in

Error message

This instance of 'CircuitBreakerStateProvider' is already initialized and cannot be used in a different circuit-breaker strategy.

What it means

A CircuitBreakerStateProvider instance can be bound to exactly one circuit-breaker strategy. The binding happens lazily during ResiliencePipelineBuilder.Build() via the internal Initialize() method, which refuses to overwrite a non-null _circuitStateProvider. Passing the same provider instance into two different CircuitBreakerStrategyOptions causes the second Build() to throw this InvalidOperationException. It protects the provider from reporting the wrong circuit's state.

Source

Thrown at src/Polly.Core/CircuitBreaker/CircuitBreakerStateProvider.cs:14

namespace Polly.CircuitBreaker;

/// <summary>
/// Allows retrieval of the circuit breaker state.
/// </summary>
public sealed class CircuitBreakerStateProvider
{
    private Func<CircuitState>? _circuitStateProvider;

    internal void Initialize(Func<CircuitState> circuitStateProvider)
    {
        if (_circuitStateProvider != null)
        {
            throw new InvalidOperationException($"This instance of '{nameof(CircuitBreakerStateProvider)}' is already initialized and cannot be used in a different circuit-breaker strategy.");
        }

        _circuitStateProvider = circuitStateProvider;
    }

    /// <summary>
    /// Gets a value indicating whether the state provider is initialized.
    /// </summary>
    /// <remarks>
    /// The initialization happens when the circuit-breaker strategy is attached to this class.
    /// This happens when the final strategy is created by the <see cref="ResiliencePipelineBuilder.Build"/> call.
    /// </remarks>
    internal bool IsInitialized => _circuitStateProvider != null;

    /// <summary>
    /// Gets the state of the underlying circuit.
    /// </summary>
    public CircuitState CircuitState => _circuitStateProvider?.Invoke() ?? CircuitState.Closed;

View on GitHub (pinned to d0e46bdb1e)

Solutions

  1. Create a new CircuitBreakerStateProvider instance for each circuit-breaker strategy instead of reusing one.
  2. If you need one logical view over several circuits, keep a dictionary of providers (one per circuit name) rather than sharing a single instance.
  3. Audit all CircuitBreakerStrategyOptions declarations to confirm no two reference the same StateProvider object reference.

Example fix

// before
var provider = new CircuitBreakerStateProvider();
optionsA.StateProvider = provider;
optionsB.StateProvider = provider; // throws on second Build()

// after
optionsA.StateProvider = new CircuitBreakerStateProvider();
optionsB.StateProvider = new CircuitBreakerStateProvider();
Defensive patterns

Strategy: validation

Validate before calling

// Before Build(), ensure the provider is not already bound:
if (provider.IsInitialized) {
    throw new InvalidOperationException("Provider already bound to another circuit; create a new instance.");
}
// Then assign and build. Track providers in a per-circuit dictionary.

Prevention

When it happens

Trigger: Instantiating one CircuitBreakerStateProvider, assigning it to optionsA.StateProvider and optionsB.StateProvider (two separate CircuitBreakerStrategyOptions), then calling Build() on both builders. Also reusing a provider after a pipeline built with it has been constructed.

Common situations: Sharing a 'watcher' object across multiple named circuits in DI; refactoring a single circuit into two and forgetting to give each its own provider; copying options via memberwise clone that aliases the StateProvider reference.

Related errors


AI-assisted analysis of App-vNext/Polly@d0e46bdb1e (2026-08-13). Data as JSON: /api/errors/e26ae6145d950604. Report an issue: GitHub.