dotnet/aspnetcore · critical · Error
Circuit host not initialized.
Error message
Circuit host not initialized.
What it means
Thrown inside the SignalR 'JS.SavePersistedState' handler when the server sends persisted component/application state before _circuitId has been assigned. _circuitId is set only after a successful StartCircuit invoke, so receiving SavePersistedState before that point means the circuit handshake ran out of order or against an uninitialized circuit.
Source
Thrown at src/Components/Web.JS/src/Platform/Circuits/CircuitManager.ts:166
connection.on('JS.AttachComponent', (componentId, selector) => attachRootComponentToLogicalElement(WebRendererId.Server, this.resolveElement(selector), componentId, false));
connection.on('JS.BeginInvokeJS', (asyncHandle: number, ...rest: unknown[]) => {
if (asyncHandle !== 0) {
this.changeActivity(1);
}
(this._dispatcher.beginInvokeJSFromDotNet as (...a: unknown[]) => unknown)
.call(this._dispatcher, asyncHandle, ...rest);
});
connection.on('JS.EndInvokeDotNet', (asyncCallId: string, success: boolean, resultJsonOrExceptionMessage: string) => {
if (asyncCallId) {
this.changeActivity(-1);
}
this._dispatcher.endInvokeDotNetFromJS(asyncCallId, success, resultJsonOrExceptionMessage);
});
connection.on('JS.ReceiveByteArray', this._dispatcher.receiveByteArray.bind(this._dispatcher));
connection.on('JS.SavePersistedState', (circuitId: string, components: string, applicationState: string) => {
if (!this._circuitId) {
throw new Error('Circuit host not initialized.');
}
if (circuitId !== this._circuitId) {
throw new Error(`Received persisted state for circuit ID '${circuitId}', but the current circuit ID is '${this._circuitId}'.`);
}
this._persistedCircuitState = { components, applicationState };
return true;
});
connection.on('JS.BeginTransmitStream', (streamId: number) => {
const readableStream = new ReadableStream({
start: (controller) => {
this.changeActivity(1);
connection.stream('SendDotNetStreamToJS', streamId).subscribe({
next: (chunk: Uint8Array) => controller.enqueue(chunk),
complete: () => { controller.close(); this.changeActivity(-1); },
error: (err) => { controller.error(err); this.changeActivity(-1); },
});
},View on GitHub (pinned to 294cab2f9b)
Solutions
- Ensure the blazor.web.js / blazor.server.js version exactly matches the ASP.NET Core server version shipping the app.
- Let the framework own circuit lifecycle; do not manually pause/resume or send state out of band.
- On persistent errors, force a full page reload to re-establish a clean circuit handshake.
Defensive patterns
Strategy: validation
Prevention
- Keep blazor.*.js version aligned with the server framework version.
- Let the framework own circuit startup ordering; do not inject state out of band.
- On persistent startup errors, force a full page reload.
When it happens
Trigger: Server-initiated persisted-state push arriving during/after a connection race, a reconnect/restart sequence where the prior circuit was not fully established, or a server version mismatch sending SavePersistedState earlier than the client expects.
Common situations: Circuit resumption (pause/resume) flows under poor connectivity; running a newer server against an older blazor.*.js (or vice versa); custom reconnection handlers that interfere with circuit startup ordering.
Related errors
- Received persisted state for circuit ID '${circuitId}', but
- Blazor Server has already started.
- Cannot start the circuit until Blazor Server has started.
- Cannot start a disposed circuit.
- Circuit options have already been configured.
AI-assisted analysis of dotnet/aspnetcore@294cab2f9b (2026-08-06).
Data as JSON: /api/errors/4fa762bf46f52514.
Report an issue: GitHub.