Closed
Description
Currently it doesn't work to do this:
app.UseExceptionHandler("/Error");
... where /Error
corresponds to a Razor Component like:
@page "/Error"
<h1>This is the error page</h1>
In practice what happens is that:
- Assume some other Razor Component endpoint (SSR) throws an unhandled exception during rendering
ExceptionHandlerMiddlewareImpl
catches the original unhandled exception, changes the endpoint to/Error
(a Razor Component endpoint), and re-runs the pipeline- We get back into
EndpointHtmlRenderer.InitializeStandardComponentServicesAsync
which tries to initialize the same scoped services for a second time. - They don't support being initialized for a second time. The first thing that happens to throw is
NavigationManager.Initialize
(as reported at HttpNavigationManager already initialized on Blazor SSR. #49456) - The system falls back on showing the standard error page
Difficulties
This isn't straightforward to resolve for two main reasons:
- It really isn't valid to reinitialize or even reuse the same scoped services when re-running the pipeline. The concept of re-running the pipeline without creating a new DI scope is extremely dubious since by definition, the old DI scope is now in an undefined error state - for security, Blazor Server is extremely careful not to let things keep running once they get into an undefined state.
- Possible solution: We could change
RazorComponentEndpointInvoker
to guarantee we create a new DI scope for the error endpoint. This would be inconsistent with MVC etc but at least safer. And would solve the double-initialization exceptions.
- Possible solution: We could change
- Even if we can make the endpoint render, it's going to behave badly if you have an interactive router in your layout. Consider:
- Some Razor Component endpoint at
/MyComponentThatThrows
throws an unhandled exception - The error middleware runs
Error.razor
, which uses the sameLayout.razor
, which contains a@rendermode=Server
router and<script src="blazor.web.js"></script>
- The error page starts up a new circuit and runs the router on the server.
- It will still see the current URL
/MyComponentThatThrows
and hence now re-runs the same component that throws a second time - Possible solution: We somehow would have to emit info into the error page markup to tell the JS-side code not to start any interactive runtime. We might get away with just putting that into the default project template but it would be nasty and error prone if people try to create their own error page from scratch.
- Some Razor Component endpoint at