dotnet / dotnet/aspnetcore

[Blazor Hybrid] Consider guarding HandleComponentException when Renderer is Disposed

Open
#66,322 1 comment 0 reactions 0 assignees View on GitHub
area-blazor bug investigate
Dominant language
C#
Stars
38.4k
Forks
10.9k
Avg merge
2d 6h
Merged PRs (30d)
290

Description

### Is there an existing issue for this?

- [x] I have searched the existing issues

### Describe the bug

I originally reported this issue on the .NET MAUI side: https://github.com/dotnet/maui/issues/34855. While investigating, I encountered several exceptions, and I believe the specific case described below belongs in this repository.

Consider the following:

```csharp
@code {
private bool _loading;

protected override async Task OnInitializedAsync()
{
await base.OnInitializedAsync();
_ = BackgroundWork();
}

private async Task BackgroundWork()
{
_loading = true;
await InvokeAsync(StateHasChanged);

try
{
await Task.Delay(5000);
throw new Exception("Exception");
}
catch (Exception exception)
{
// MainLayout needs to be wrapped in ErrorBoundary
await DispatchExceptionAsync(exception);
}
finally
{
_loading = false;
await InvokeAsync(StateHasChanged);
}
}
}
```

If the browser is closed while `Task.Delay` is still in progress and the WebView is **explicitly disposed**(!!!) via `BlazorWebView.DisposeAsync()`, an exception `Renderer does not have a component with ID XX` is thrown.
If the WebView is not explicitly disposed, a different exception occurs instead, related to the `WebView2` control already being disposed.

This behavior has been reproduced in WPF. I have not tested WinForms, but I expect similar behavior since most of the code is shared (aside from the dispatcher). I have also not verified whether this affects MAUI Hybrid.

### Expected Behavior

Not to throw exception when webview is disposed.

### Steps To Reproduce

This issue can be reproduced using a shared sample with .NET MAUI.

1. Fork the repository: https://github.com/ScarletKuro/WpfIssueThreading
2. In `App.xaml.cs`, uncomment the line `mainWindow.DisposeAsync();`
3. Run the application
4. Close the window while it says loading
5. Observe the exception: `Renderer does not have a component with ID XX`

### Exceptions (if any)

```
Exception has been thrown by the target of an invocation. System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation.
---> System.ArgumentException: The renderer does not have a component with ID 15.
at Microsoft.AspNetCore.Components.RenderTree.Renderer.GetRequiredComponentState(Int32 componentId)
at Microsoft.AspNetCore.Components.RenderTree.Renderer.HandleComponentException(Exception exception, Int32 componentId)
at Microsoft.AspNetCore.Components.RenderHandle.<>c__DisplayClass22_0.b__0()
at Microsoft.AspNetCore.Components.WebView.Wpf.WpfDispatcher.InvokeAsync(Action workItem)
at SalesSystem.Web.Pages.Kiosk.GetCouponsInBackground() in C:\Users\Kuro\source\repos\kiisu\src\SalesSystem.Web\Pages\Kiosk.razor.cs:line 129
at SalesSystem.Web.Pages.Kiosk.GetCouponsInBackground() in C:\Users\Kuro\source\repos\kiisu\src\SalesSystem.Web\Pages\Kiosk.razor.cs:line 138
at Microsoft.AspNetCore.Components.WebView.Wpf.WpfDispatcher.<>c.<.cctor>b__8_0(Exception exception)
at InvokeStub_Action`1.Invoke(Object, Span`1)
at System.Reflection.MethodBaseInvoker.InvokeWithOneArg(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
--- End of inner exception stack trace ---
at System.Reflection.MethodBaseInvoker.InvokeWithOneArg(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
at System.Delegate.DynamicInvokeImpl(Object[] args)
at System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs)
at System.Windows.Threading.ExceptionWrapper.TryCatchWhen(Object source, Delegate callback, Object args, Int32 numArgs, Delegate catchHandler)
System.Object:
System.ArgumentException: The renderer does not have a component with ID 15.
at Microsoft.AspNetCore.Components.RenderTree.Renderer.GetRequiredComponentState(Int32 componentId)
at Microsoft.AspNetCore.Components.RenderTree.Renderer.HandleComponentException(Exception exception, Int32 componentId)
at Microsoft.AspNetCore.Components.RenderHandle.<>c__DisplayClass22_0.b__0()
at Microsoft.AspNetCore.Components.WebView.Wpf.WpfDispatcher.InvokeAsync(Action workItem)
at SalesSystem.Web.Pages.Kiosk.GetCouponsInBackground() in C:\Users\Kuro\source\repos\kiisu\src\SalesSystem.Web\Pages\Kiosk.razor.cs:line 129
at SalesSystem.Web.Pages.Kiosk.GetCouponsInBackground() in C:\Users\Kuro\source\repos\kiisu\src\SalesSystem.Web\Pages\Kiosk.razor.cs:line 138
at Microsoft.AspNetCore.Components.WebView.Wpf.WpfDispatcher.<>c.<.cctor>b__8_0(Exception exception)
at InvokeStub_Action`1.Invoke(Object, Span`1)
at System.Reflection.MethodBaseInvoker.InvokeWithOneArg(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
```

### .NET Version

10.0.201

### Anything else?

_No response_

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.