Skip to content

Conversation

@oroztocil
Copy link
Member

In .NET 10, resuming a Blazor circuit fails when the server has been restarted (and an out-of-process persistent storage is not used). Currently, this is handled on the client side by displaying a "reconnection failed" UI which requires the user to manually reload the page.

This creates a regression in the default behavior compared to .NET 9, particularly for local development scenarios. Previously, the page would reload automatically once the server has restarted.

This PR re-adds the default reloading behavior to:

  • the ReconnectModal component used in the Blazor project template,
  • the DefaultReconnectDisplay implementation used in the framework as the fallback when no reconnection UI is found in the user code.

Fixes #64228

@oroztocil oroztocil requested a review from a team as a code owner November 10, 2025 18:01
Copilot AI review requested due to automatic review settings November 10, 2025 18:01
@github-actions github-actions bot added the area-blazor Includes: Blazor, Razor Components label Nov 10, 2025
@oroztocil oroztocil requested a review from javiercn November 10, 2025 18:02
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR changes the behavior when resuming a Blazor circuit fails by automatically reloading the page instead of prompting the user to manually reload.

  • Automatic page reload on resume failure in DefaultReconnectDisplay
  • Support for "resume-failed" state in project template's reconnect modal handler

Reviewed Changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.

File Description
DefaultReconnectDisplay.ts Changed failed() method to automatically reload the page when resume fails, instead of showing reload UI elements
ReconnectModal.razor.js Added handling for "resume-failed" state to trigger automatic page reload

@oroztocil
Copy link
Member Author

oroztocil commented Nov 12, 2025

@ilonatommy I pushed few changes in DefaultReconnectDisplay.ts that are intended to improve clarity of the operations:

  • Instead of using a boolean flag reconnect, I use the union property specifying type of the operation ("reconnect" or "pause") that was already used in the ReconnectDisplayUpdateOptions that we are passing in.
  • I switched the if/else branches so that "pause" is checked explicitly and "reconnect" is used as the else fallback. This makes more sense to me as "reconnect" was the only operation in the past and logically serves as the backwards-compatible branch. (This was not necessary due to the way how the reconnect flag was used before this commit, however, this is clearer I think.)
  • I renamed reloadButton to retryButton. This is unrelated but it bugged and confused when I was reading the code because it is actually a button with the label "Retry" and it does no reload the page but calls the retry method.

Concerning our previous discussion, adding the automatic location.reload() on failed resume is the point of this PR and it is necessary to fix the regression in local dev scenario reported in #64228.

Comment on lines 16 to 17
remote: boolean,
graceful: boolean
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remote and graceful represent the same concept. remote resumes are always ungraceful and vice versa.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see, I thought those were orthogonal properties. Then it simplifies the change. I will update the PR.

} else if (event.detail.state === "failed") {
document.addEventListener("visibilitychange", retryWhenDocumentBecomesVisible);
} else if (event.detail.state === "rejected") {
} else if (event.detail.state === "rejected" || (event.detail.state === "resume-failed" && !!event.detail.graceful)) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
} else if (event.detail.state === "rejected" || (event.detail.state === "resume-failed" && !!event.detail.graceful)) {
} else if (event.detail.state === "rejected" || (event.detail.state === "resume-failed" && event.detail.remote))

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@javiercn your earlier suggestion of just
} else if (event.detail.state === "rejected" || event.detail.state === "resume-failed") {
provides a better empty new circuit experience.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, not sure what you mean. We don't want to reload the page unconditionally when the circuit fails to resume. We only want to do that when it happens as a result of the app disconnecting, but not when the app explicitly pauses the circuit.

For ungraceful disconnections the client is not in control, but for pausing and then resuming, the client is. We don't want to unconditionally reload the page by default because that precludes users from saving any data that they had on the page.

Example, you are filling in a long form field, you switch to a different tab. Your JS code "pauses" the circuit when the visibility changes. When the user comes back to the tab and you try to "resume" the circuit, if for some reason the resume operation fails, we don't want to result in a refresh.

In the case of reconnection, the client app is not in control, but in case of reconnection, the client triggered the "pause" operation in the first place and should have some code to handle things when the "resume" goes wrong.

I would argue that automatically reloading is also a bad policy to have for reconnection, as you run into a similar problem, but I think that ship has sailed.

We want to restore the behavior for failed reconnections + failed resumes by default, but we don't want to automatically reload the page in case of failed resumes when the app triggered the pause.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All good @javiercn. I would much rather wait until you guys released the fix and then I deal with any smaller changes on my side. The code we are talking about is in the project so it can be tweaked, if need be, which is great about the new design.

The problem still is that because of the exception in the TS module, my app shows the uncaught exception error before reconnecting, which is not great from a user experience.

I asked in the other PR, but this seems the more appropriate PR, when do you recon this fix will be available?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@snympi We target the January servicing patch. (For context, code freeze for the December patch has already happened.)

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

Labels

area-blazor Includes: Blazor, Razor Components

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Blazor Server app in .NET 10 fails to reconnect after server restart, unlike .NET 9

5 participants