aspnetcore icon indicating copy to clipboard operation
aspnetcore copied to clipboard

Blazor SSR Task throws NavigationException from finalizer thread

Open EffectivelyEfficient opened this issue 8 months ago • 5 comments

Is there an existing issue for this?

  • [x] I have searched the existing issues

Describe the bug

When Blazor is used with SSR, and the OnInitializedAsync method throws an error such as the NavigationException, the task can end up on the finalizer thread.

It appears to require the OnInitializedAsync method to have made an async call first. However, even this is not reliable reproduction.

When I call NavigateTo() in OnInitializedAsync (server side) the browser is successfully redirected using navigationManager.NavigateTo(), but the NavigationException still hits the finalizer thread. In production it can take up to 22 minutes before the I saw it hit the "unobserved exception was rethrown by the finalizer thread" error.

Expected Behavior

NavigationException should be caught by the blazor code to trigger navigation. It should not be rethrown by the finalizer thread.

Steps To Reproduce

  • Create an OnInitializedAsync method with 2s delay
  • Then call navigationManager.NavigateTo("/");
  • Call GC.Collect and WaitForPendingFinalizers to ensure prompt reproduction

Load the following example in the web browser. It should redirect to itself. The second time it is called, the errors should be in the console.

Index.razor

@page "/"
@using BlazorApp1

<h1>Hello, world!</h1>

@code {
	[Inject]
	protected NavigationManager navigationManager { get; set; } = default!;

	protected override async Task OnInitializedAsync() {
		GC.Collect();
		GC.WaitForPendingFinalizers();
		GC.Collect();
		await Task.Delay(2000);
		navigationManager.NavigateTo("/");
	}
}

App.razor

@using Microsoft.AspNetCore.Components.Routing
@using Microsoft.AspNetCore.Components.Web
@using BlazorApp1

<Router AppAssembly="@typeof(App).Assembly">
    <Found Context="routeData">
        <RouteView RouteData="@routeData"/>
    </Found>
</Router>

Program.cs:

using BlazorApp1;
using System.Runtime.InteropServices;

Console.WriteLine("Framework: " + RuntimeInformation.FrameworkDescription);
Console.WriteLine("Runtime Identifier: " + RuntimeInformation.RuntimeIdentifier);

AppDomain.CurrentDomain.UnhandledException += (sender, e) => {
		Console.WriteLine("Unhandled caught");
};

TaskScheduler.UnobservedTaskException += (sender, e) => {
	Console.WriteLine("Unhandled caught: " + e.Exception.ToString());
	e.SetObserved();
};

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorComponents();
var app = builder.Build();
app.UseStaticFiles();
app.UseRouting();
app.UseAntiforgery();
app.MapRazorComponents<App>();
app.Run();

BlazorApp1.csproj:

  <PropertyGroup>
    <TargetFramework>net9.0</TargetFramework>
    <Nullable>enable</Nullable>
    <ImplicitUsings>enable</ImplicitUsings>
  </PropertyGroup>
	<ItemGroup>
	</ItemGroup>
</Project>

Exceptions (if any)

info: Microsoft.AspNetCore.Hosting.Diagnostics[1]
      Request starting HTTP/2 GET https://localhost:7275/ - - -
info: Microsoft.AspNetCore.Routing.EndpointMiddleware[0]
      Executing endpoint '/ (/)'
Unhandled caught: System.AggregateException: A Task's exception(s) were not observed either by Waiting on the Task or accessing its Exception property. As a result, the unobserved exception was rethrown by the finalizer thread. (Exception of type 'Microsoft.AspNetCore.Components.NavigationException' was thrown.)
 ---> Microsoft.AspNetCore.Components.NavigationException: Exception of type 'Microsoft.AspNetCore.Components.NavigationException' was thrown.
   at Microsoft.AspNetCore.Components.Endpoints.HttpNavigationManager.NavigateToCore(String uri, NavigationOptions options)
   at Microsoft.AspNetCore.Components.NavigationManager.NavigateToCore(String uri, Boolean forceLoad)
   at Microsoft.AspNetCore.Components.NavigationManager.NavigateTo(String uri, Boolean forceLoad, Boolean replace)
   at BlazorApp1.Pages.Index.OnInitializedAsync() in C:\Users\xxx\source\repos\bugcase\BlazorApp1\BlazorApp1\Pages\Index.razor:line 15
   at Microsoft.AspNetCore.Components.ComponentBase.RunInitAndSetParametersAsync()
   at Microsoft.AspNetCore.Components.RenderTree.Renderer.GetErrorHandledTask(Task taskToHandle, ComponentState owningComponentState)
   at Microsoft.AspNetCore.Components.RenderTree.Renderer.GetErrorHandledTask(Task taskToHandle, ComponentState owningComponentState)
   at Microsoft.AspNetCore.Components.Endpoints.EndpointHtmlRenderer.<WaitForNonStreamingPendingTasks>g__Execute|43_0()
   at Microsoft.AspNetCore.Components.Endpoints.EndpointHtmlRenderer.WaitForResultReady(Boolean waitForQuiescence, PrerenderedComponentHtmlContent result)
   at Microsoft.AspNetCore.Components.Endpoints.EndpointHtmlRenderer.RenderEndpointComponent(HttpContext httpContext, Type rootComponentType, ParameterView parameters, Boolean waitForQuiescence)
   --- End of inner exception stack trace ---
info: Microsoft.AspNetCore.Routing.EndpointMiddleware[1]
      Executed endpoint '/ (/)'
info: Microsoft.AspNetCore.Hosting.Diagnostics[2]
      Request finished HTTP/2 GET https://localhost:7275/ - 302 0 text/html;+charset=utf-8 2476.9080ms

.NET Version

9.0.300

Anything else?

dotnet --version 9.0.300

Microsoft Visual Studio Community 2022 (64-bit) - Version 17.14.3

dotnet --info .NET SDK: Version: 9.0.300 Commit: 15606fe0a8 Workload version: 9.0.300-manifests.af4147de MSBuild version: 17.14.5+edd3bbf37

Runtime Environment: OS Name: Windows OS Version: 10.0.26100 OS Platform: Windows RID: win-x64 Base Path: C:\Program Files\dotnet\sdk\9.0.300\

.NET workloads installed: [aspire] Installation Source: VS 17.14.36127.28 Manifest Version: 8.2.2/8.0.100 Manifest Path: C:\Program Files\dotnet\sdk-manifests\8.0.100\microsoft.net.sdk.aspire\8.2.2\WorkloadManifest.json Install Type: Msi

Configured to use loose manifests when installing new manifests.

Host: Version: 9.0.5 Architecture: x64 Commit: e36e4d1a8f

.NET SDKs installed: 9.0.300 [C:\Program Files\dotnet\sdk]

.NET runtimes installed: Microsoft.AspNetCore.App 6.0.36 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 7.0.20 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 8.0.16 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 9.0.5 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.NETCore.App 6.0.36 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 7.0.20 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 8.0.16 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 9.0.5 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.WindowsDesktop.App 6.0.36 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App] Microsoft.WindowsDesktop.App 7.0.20 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App] Microsoft.WindowsDesktop.App 8.0.16 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App] Microsoft.WindowsDesktop.App 9.0.5 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]

Other architectures found: x86 [C:\Program Files (x86)\dotnet] registered at [HKLM\SOFTWARE\dotnet\Setup\InstalledVersions\x86\InstallLocation]

Environment variables: Not set

global.json file: Not found

Learn more: https://aka.ms/dotnet/info

Download .NET: https://aka.ms/dotnet/download

EffectivelyEfficient avatar May 30 '25 06:05 EffectivelyEfficient

@EffectivelyEfficient thanks for contacting us.

We'll take a look and see what's going on.

For 10.0 we are changing the way this works on SSR to avoid throwing an exception at all. I'm puzzled about how this can even happen.

Do you happen to have the project you created to describe the issue? Could you please push it to a public github repository? That help us ensure that we don't miss or mess anything up when we try things on our end.

javiercn avatar May 30 '25 08:05 javiercn

Here is a minimal repro project that illustrates the problem. https://github.com/EffectivelyEfficient/BugBlazorNavigationExceptionFromFinalizer On my machine it reproduces the problem reliably and within a few seconds.

EffectivelyEfficient avatar May 30 '25 10:05 EffectivelyEfficient

Also note this is for a non-streaming scenario. This means there is no @attribute [StreamRendering] on Index.razor. Putting @attribute [StreamRendering] on Index.razor stops the error, but it also prevents navigationManager.NavigateTo() from redirecting.

EffectivelyEfficient avatar May 30 '25 11:05 EffectivelyEfficient

@EffectivelyEfficient thanks for the additional details.

We'll take a look and update the issue accordingly.

javiercn avatar May 31 '25 17:05 javiercn

I'm puzzled about how this can even happen.

@javiercn, logging this internal exception is an expected behavior in net9 in this setup and this reproduction just shows that it's being thrown. I think I don't understand what you intended to do here. The only option that comes to my mind is to backport the fix: https://github.com/dotnet/aspnetcore/pull/61306 + https://github.com/dotnet/aspnetcore/pull/62358.

ilonatommy avatar Jun 18 '25 15:06 ilonatommy