Skip to content

Fixed circuit disconnects caused by navigation during slow custom CircuitHandler execution - #68633

Draft
BrundhaVelusamy wants to merge 2 commits into
dotnet:mainfrom
BrundhaVelusamy:59657_CircuitHandler
Draft

Fixed circuit disconnects caused by navigation during slow custom CircuitHandler execution#68633
BrundhaVelusamy wants to merge 2 commits into
dotnet:mainfrom
BrundhaVelusamy:59657_CircuitHandler

Conversation

@BrundhaVelusamy

Copy link
Copy Markdown

Bug Description

When a custom CircuitHandler has latency in OnCircuitOpenedAsync/OnConnectionUpAsync (e.g., await Task.Delay(...)), navigating to a different page before the handler completes can permanently break the circuit, surfacing to the user as "Circuit has been shut down due to error".

Root cause: while the initial UpdateRootComponents batch is suspended awaiting the slow handler, the renderer's dispatcher yields and a second batch — triggered by the navigation — runs concurrently. This second batch can send Update/Remove operations for SSR component IDs that the first (still in-flight) batch hasn't registered yet, and the existing WebRootComponentManager.UpdateRootComponentAsync/RemoveRootComponent throw InvalidOperationException for unregistered IDs. That exception propagates up and fires CircuitHost.UnhandledException, tearing down the circuit even though the client is otherwise healthy.

Description of code changes

WebRootComponentManager.cs

  • Added TryRemoveRootComponent(int ssrComponentId) — returns false instead of throwing when the component was never registered.
  • Added TryUpdateOrAddRootComponentAsync(...) — falls back to AddRootComponentAsync instead of throwing when the component was never registered.
  • Existing RemoveRootComponent/UpdateRootComponentAsync are unchanged and still used by other callers.

CircuitHost.cs

  • PerformRootComponentOperations now calls TryRemoveRootComponent/TryUpdateOrAddRootComponentAsync for Remove/Update operations instead of the throwing variants.
  • Added _updateRootComponentsGeneration, an integer incremented on every UpdateRootComponents call. If a newer batch (e.g., from navigation) completes while the current batch was blocked on circuit handlers, the stale batch acknowledges the client and returns early instead of re-rendering over the already-updated page.
  • Cancellation-related guards (!cancellation.IsCancellationRequested) around the client ack and UnhandledException invocation so a transient connection drop during a slow handler doesn't tear down an otherwise-recoverable circuit.

Output

Before changes

59657_BeforeFix.mp4

After changes

59657_AfterFix.mp4

Fixes #59657

@dotnet-policy-service dotnet-policy-service Bot added the community-contribution Indicates that the PR has been added by a community member label Aug 19, 2026
@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Thanks for your PR, @BrundhaVelusamy. Someone from the team will get assigned to your PR shortly and we'll get it reviewed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

community-contribution Indicates that the PR has been added by a community member

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Blazor] Custom CircuitHandler latency breaks WS connection on navigation

1 participant