Skip to content

Blazor CircuitHandler (Client-Side JS) not Triggering onCircuitClosed in Case of Server-Side Exceptions #54807

@AeonSake

Description

@AeonSake

Is there an existing issue for this?

  • I have searched the existing issues

Describe the bug

When the server runs into an unhandled exception, the client's circuit is forcefully closed by the client via a message sent to the client. However, the onCircuitClosed function of the CircuitHandler handles attached with the Blazor.start() options is not triggered in this case. From what I can tell, there is no actual way to get informed that the circuit was forcefully closed in this way via code (other than maybe observing style changes to the error UI element).

Cause

Unhandled server-side exceptions are handled by notifying the client via a custom event (JS.Error) which then closes the connection from the client side.

Event is handled here:

connection.on('JS.Error', error => {
this._renderingFailed = true;
this.unhandledError(error);
showErrorNotification();
});

Which calls unhandledError():

private unhandledError(err: Error): void {
this._logger.log(LogLevel.Error, err);
// Disconnect on errors.
// Trying to call methods on the connection after its been closed will throw.
this.disconnect();
}

Expected Behavior

The onCircuitClosed function is invoked, informing all handles that the circuit was closed. Ideally I would even like to have the close reason as argument (enum? or the error object/message) so the client can react to these cases (e.g., by reloading the page). The CircuitHandler interface could also be extended with something like an onCircuitFaulted function.

Steps To Reproduce

Server Setup

TestPage.razor:

@page "/test"
@implements IDisposable

<a href="/">Home</a>

@code {
    public void Dispose() => throw new Exception();
}

_Host.cshtml:

<!-- ... -->
<body>
    <!-- ... -->

    <script src="_framework/blazor.server.js" autostart="false"></script>
    <script>
        Blazor.start({
            circuitHandlers: [{
                 onCircuitOpened: () => console.log('circuit opened'),
                 onCircuitClosed: () => console.log('circuit closed')
            }]
        });
    </script>
</body>
</html>

Procedure

Navigate to /test and then press the Home link.

Result

The onCircuitClosed function is never invoked and the console message is not printed. The Page gets stuck in its last render state before the circuit was closed. However, the error UI element with the ID blazor-error-ui will receive the appropriate style changes (style set to display:block).

Notes

The component does not have to be a page (just for demonstration), also works with normal components that are disposed at some point. Also works with Exceptions thrown inside IAsyncDisposable implementations. In theory, all unhandled exceptions will cause this issue. The reason for why I selected the dispose pattern as source for the exception is because not even an ErrorBoundary component will catch those exceptions and because I had some occurrences where async disposing of JS module references failed and caused the circuit to fault. While I understand that the later can be circumvented via a try-catch block, I still need a way to automatically reloading the page in case of errors since in my case, the client is in a stand-alone kiosk-mode and I cannot rely on a user clicking the reload button in the error UI.

Exceptions (if any)

No response

.NET Version

8.0.202

Anything else?

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    area-blazorIncludes: Blazor, Razor Componentshelp wantedUp for grabs. We would accept a PR to help resolve this issue

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions