dotnet/aspnetcore · error · Error

Circuit state ${this._stateName} not initialized

Error message

Circuit state ${this._stateName} not initialized

What it means

`CircuitState.complete(value)` resolves the in-flight operation's promise. It throws if `_resolve` is undefined, i.e. no operation was started with `reset()` (or the prior operation already completed/failed and cleared the resolvers). Calling `complete` on an uninitialized state would resolve a non-existent promise, so it fails fast.

Source

Thrown at src/Components/Web.JS/src/Platform/Circuits/CircuitManager.ts:699

  private _reject?: (reason: any) => void;

  private _lastValue?: T;

  public reset(): void {
    if (this._promise) {
      throw new Error(`Circuit state ${this._stateName} is already in progress`);
    }

    const { promise, resolve, reject } = Promise.withResolvers<T>();
    this._promise = promise;
    this._resolve = resolve;
    this._reject = reject;
    this._lastValue = this._resetValue;
  }

  public complete(value: T): void {
    if (!this._resolve) {
      throw new Error(`Circuit state ${this._stateName} not initialized`);
    }

    const resolve = this._resolve;
    this._lastValue = value;

    this._promise = undefined;
    this._resolve = undefined;
    this._reject = undefined;

    resolve(value);
  }

  public fail(reason: any): void {
    if (!this._reject) {
      throw new Error(`Circuit state ${this._stateName} not initialized`);
    }

    const reject = this._reject;

View on GitHub (pinned to 3600ca084e)

Solutions

  1. Always pair `reset()` with exactly one terminal call (`complete` or `fail`); guard terminal calls with `isInprogress()`.
  2. Ensure error and success paths cannot both run — use try/catch/finally that resolves the state once.
  3. Add `if (state.isInprogress()) state.complete(...)` around server-driven completions to tolerate out-of-order messages.
  4. Trace which SignalR handler invoked `complete` to find the duplicated/out-of-order settlement.

Example fix

// before
this._pausingState.complete(true); // throws if not in progress

// after
if (this._pausingState.isInprogress()) {
  this._pausingState.complete(true);
}
Defensive patterns

Strategy: validation

Validate before calling

function safeComplete<T>(s: { isInprogress(): boolean; complete(v: T): void }, v: T) {
  if (s.isInprogress()) s.complete(v);
}

Try / catch

try { state.complete(value); }
catch (e) { if (/not initialized/.test(e.message)) { /* already settled - ignore */ return; } throw e; }

Prevention

When it happens

Trigger: `complete()` is called on a `CircuitState` whose `reset()` was never called, or was already completed/failed (which clears `_resolve`).

Common situations: A server callback (`JS.RenderBatch`/`JS.RequestPause` completion) firing for a state that was never started or already settled; a logic bug double-completing a pause/resume; an error path that already called `fail()` then a success path calls `complete()`.

Related errors


AI-assisted analysis of dotnet/aspnetcore@3600ca084e (2026-08-11). Data as JSON: /api/errors/dc2085a3e1f0052d. Report an issue: GitHub.