dotnet/aspnetcore · critical · Error
Circuit host not initialized.
Error message
Circuit host not initialized.
What it means
The `JS.SavePersistedState` SignalR handler receives persisted component/application state from the server (used when a circuit is paused/resumed). It requires a live circuit id (`_circuitId`) to attach the state to; if `_circuitId` is undefined the message arrived before `startCore` assigned one (or after it was cleared), so the handler refuses to record state for an unknown circuit.
Solutions
- Ensure `Blazor.start()` has fully resolved (circuit started) before triggering flows that persist state (pause/resume, navigations that force state save).
- Check server logs for why `StartCircuit` may have returned an empty id (server refusing circuit creation) and resolve the root cause.
- On reconnect, let the framework re-run its start sequence rather than manually re-sending state messages.
- Verify you are on a supported Blazor version — the persisted-state/pause-resume feature requires matching client and server builds.
Defensive patterns
Strategy: validation
Validate before calling
// internal handler guard pattern
if (!this._circuitId) { this._logger.log(LogLevel.Warning, 'Persisted state received before circuit started.'); return; } Try / catch
try { /* persisted-state handling */ }
catch (e) { if (/not initialized/.test(e.message)) { /* ignore stale state push */ return; } throw e; } Prevention
- Let Blazor.start() fully resolve before triggering state-persisting flows.
- Keep client and server builds matched for the pause/resume feature.
- Investigate empty StartCircuit ids in server logs.
- Avoid manually sending persisted-state messages.
When it happens
Trigger: Server sends `JS.SavePersistedState` before `StartCircuit` returned a non-empty `_circuitId`, or the message is processed on a connection whose `startCore` returned falsy (no circuit established) but the server still pushed state.
Common situations: Race between an early persisted-state push and circuit start on slow/reconnecting links; server-side state preservation firing during a reconnect window where the client hasn't reconfirmed its circuit id; misordered SignalR messages during circuit pause/resume flows; dev-time hot reload reinitializing the manager.
Related errors
- Cannot start a disposed circuit.
- Received persisted state for circuit ID
- Cannot start the circuit until Blazor Server has started.
- Circuit state is already in progress
- Circuit state is in progress
AI-assisted analysis of dotnet/aspnetcore@3600ca084e (2026-08-11).
Data as JSON: /api/errors/4fa762bf46f52514.
Report an issue: GitHub.
Appendix: 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 3600ca084e)