{"record":{"id":"6ec95580488c9718","repo":"dotnet/aspnetcore","slug":"received-persisted-state-for-circuit-id-circuit","errorCode":null,"errorMessage":"Received persisted state for circuit ID '${circuitId}', but the current circuit ID is '${this._circuitId}'.","messagePattern":"Received persisted state for circuit ID '(.+?)', but the current circuit ID is '(.+?)'\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"critical","filePath":"src/Components/Web.JS/src/Platform/Circuits/CircuitManager.ts","lineNumber":169,"sourceCode":"        this.changeActivity(1);\n      }\n      (this._dispatcher.beginInvokeJSFromDotNet as (...a: unknown[]) => unknown)\n        .call(this._dispatcher, asyncHandle, ...rest);\n    });\n    connection.on('JS.EndInvokeDotNet', (asyncCallId: string, success: boolean, resultJsonOrExceptionMessage: string) => {\n      if (asyncCallId) {\n        this.changeActivity(-1);\n      }\n      this._dispatcher.endInvokeDotNetFromJS(asyncCallId, success, resultJsonOrExceptionMessage);\n    });\n    connection.on('JS.ReceiveByteArray', this._dispatcher.receiveByteArray.bind(this._dispatcher));\n\n    connection.on('JS.SavePersistedState', (circuitId: string, components: string, applicationState: string) => {\n      if (!this._circuitId) {\n        throw new Error('Circuit host not initialized.');\n      }\n      if (circuitId !== this._circuitId) {\n        throw new Error(`Received persisted state for circuit ID '${circuitId}', but the current circuit ID is '${this._circuitId}'.`);\n      }\n      this._persistedCircuitState = { components, applicationState };\n      return true;\n    });\n\n    connection.on('JS.BeginTransmitStream', (streamId: number) => {\n      const readableStream = new ReadableStream({\n        start: (controller) => {\n          this.changeActivity(1);\n          connection.stream('SendDotNetStreamToJS', streamId).subscribe({\n            next: (chunk: Uint8Array) => controller.enqueue(chunk),\n            complete: () => { controller.close(); this.changeActivity(-1); },\n            error: (err) => { controller.error(err); this.changeActivity(-1); },\n          });\n        },\n      });\n\n      this._dispatcher.supplyDotNetStream(streamId, readableStream);","sourceCodeStart":151,"sourceCodeEnd":187,"githubUrl":"https://github.com/dotnet/aspnetcore/blob/3600ca084e9c8b5f4174fc5e747f4c52d2100806/src/Components/Web.JS/src/Platform/Circuits/CircuitManager.ts#L151-L187","documentation":"Inside `JS.SavePersistedState` the handler compares the incoming `circuitId` against `this._circuitId`. A mismatch means the server is sending state for a circuit this client is not (or no longer) hosting — accepting it would corrupt the local persisted-state slot, so the handler throws with both ids for diagnostics.","triggerScenarios":"Server transmits `JS.SavePersistedState` with a circuit id different from the one returned by `StartCircuit`/`ResumeCircuit` and stored in `_circuitId`. Typically a stale server-side circuit record pushing state, or a reconnect where a new circuit id was assigned but an old state push is still in flight.","commonSituations":"Reconnect/resume flows where the server created a fresh circuit but a queued persisted-state message references the previous one; load-balancer/sticky-session misconfiguration routing a client's messages to a server holding a different circuit; multiple tabs sharing a connection unexpectedly; server restart losing circuit identity.","solutions":["Ensure sticky sessions are configured for Blazor Server in a load-balanced deployment so a client always reaches the server holding its circuit.","Confirm client and server are the same build version so circuit id semantics match across resume.","Avoid manually invoking resume/reconnect concurrently from multiple code paths.","If the error recurs, capture the reported pair of ids and correlate with server circuit lifecycle logs to find the stale push."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// guard the handler\nif (circuitId !== this._circuitId) { this._logger.log(LogLevel.Error, `Stale state for ${circuitId}, have ${this._circuitId}`); return; }","typeGuard":null,"tryCatchPattern":"try { /* save persisted state */ }\ncatch (e) { if (/current circuit ID/.test(e.message)) { /* mismatch - drop stale state */ return; } throw e; }","preventionTips":["Configure sticky sessions in load-balanced Blazor Server deployments.","Match client/server Blazor versions across resume.","Do not run concurrent resume/reconnect paths.","Correlate the reported id pair with server circuit logs."],"tags":["circuit","persisted-state","signalr","load-balancing","blazor-server"],"backgroundTag":null,"analyzedSha":"3600ca084e9c8b5f4174fc5e747f4c52d2100806","analyzedAt":"2026-08-11T16:32:30.678Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}