[release/10.0] Backport fix for handling of expired client-persisted circuit state#67453
Open
oroztocil wants to merge 2 commits into
Open
[release/10.0] Backport fix for handling of expired client-persisted circuit state#67453oroztocil wants to merge 2 commits into
oroztocil wants to merge 2 commits into
Conversation
Contributor
There was a problem hiding this comment.
Pull request overview
Backports a Blazor Server circuit-resume fix to better handle expired/invalid client-persisted circuit state by validating persisted root component data during resume and hard-failing invalid resume/update flows so the client can recover (e.g., via reload).
Changes:
- Add resume-time validation/deserialization of persisted root component descriptors and attach a resumed-only persisted state model.
- Harden
UpdateRootComponentsto terminate/abort the circuit when persisted state is invalid. - Add/adjust E2E + unit tests and a test-server knob to force persisted-circuit expiration for coverage.
Reviewed changes
Copilot reviewed 10 out of 10 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| src/Components/test/testassets/Components.TestServer/RazorComponentEndpointsStartup.cs | Adds a configuration-driven retention period override to force persisted state expiry during tests. |
| src/Components/test/E2ETest/ServerExecutionTests/ServerResumeWithExpiredStateTest.cs | New E2E coverage for client pause/resume when client-held persisted state is expired. |
| src/Components/test/E2ETest/ServerExecutionTests/ServerReconnectionWithoutStateTest.cs | Refactors reconnection/pause UI handling to align with the new resume behavior and custom UI mode. |
| src/Components/Server/test/Circuits/ComponentHubTest.cs | Updates test data to provide valid (empty) root components payload for resume validation. |
| src/Components/Server/test/Circuits/CircuitRegistryTest.cs | Adjusts test to use the new resumed persisted state type. |
| src/Components/Server/test/Circuits/CircuitPersistenceManagerTest.cs | Updates unit tests for new ToRootComponentOperationBatch signature and behavior. |
| src/Components/Server/src/ComponentHub.cs | Validates persisted root component data on resume; terminates circuit when persisted resume/update data is invalid. |
| src/Components/Server/src/Circuits/ResumedPersistedCircuitState.cs | Introduces a resumed-only persisted state container holding application state + root component descriptors. |
| src/Components/Server/src/Circuits/CircuitPersistenceManager.cs | Splits descriptor validation/deserialization from operation-batch creation; fixes retention selection logic. |
| src/Components/Server/src/Circuits/CircuitHost.cs | Switches persisted-state attachment from PersistedCircuitState to ResumedPersistedCircuitState. |
ilonatommy
approved these changes
Jun 29, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Backport of #64798 to release/10.0
Fixes #67257
Description
Blazor Server supports pausing a circuit (the serialized state is handed to the client) and later resuming it. When the client tries to resume after the client-held serialized state has expired (its lifetime is bounded by
PersistedCircuitInMemoryRetentionPeriod/PersistedCircuitDistributedRetentionPeriod), the resume path failed without proper handling.Problem analysis:
UpdateRootComponents. If the persisted markers had expired, that produced a null operation batch and threw aNullReferenceException. The circuit was left half-initialized, so the page appeared restored but was frozen, and clicking any interactive element produced a silent "There is no event handler associated with this event." error. There was no error surfaced and no clean way to detect or recover.Fix:
UpdateRootComponentstoResumeCircuit, so the server can validate the state up front. If it is invalid or expired, resume fails explicitly and the client falls back to its reconnection/reload logic instead of entering the frozen state.Customer Impact
Users are hitting this in production. After a tab is left idle past the retention period, resuming returns the user to an apparently normal page that is actually frozen: interactive elements do nothing and there is often no error at all, so the failure is unexpected and very hard to diagnose.
There is no clean client-side workaround: the issue cannot be fixed from JavaScript because the full fix requires the server-side change in this backport, and the suggested mitigations are hacky and unreliable.
Multiple customers are reporting and asking for a .NET 10 backport: #64607, #67257 (tracking the backport request), and #64228 (a different reconnection issue whose later comments describe this exact pause/resume freeze, with customers confirming there is no usable workaround).
Regression?
This is a user-facing behavior that did not exist prior to .NET 10 (introduced with circuit pause/resume persistence).
Risk
The change touches the circuit resume path. Risk is mitigated: the fix has been released in .NET 11 since Preview 1 with no bug reports filed against it, and all changes are internal (no public API changes).
Verification
New E2E tests reproduce both the happy path (resume restores state and stays interactive) and the exact issue (resume with expired state reloads cleanly instead of freezing). Verified manually as well.
Packaging changes reviewed?