Skip to content

ErrorBoundary does not work #56413

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
1 task done
garrettlondon1 opened this issue Jun 23, 2024 · 23 comments
Open
1 task done

ErrorBoundary does not work #56413

garrettlondon1 opened this issue Jun 23, 2024 · 23 comments
Labels
area-blazor Includes: Blazor, Razor Components enhancement This issue represents an ask for new feature or an enhancement to an existing one feature-blazor-error-boundary triaged
Milestone

Comments

@garrettlondon1
Copy link

Is there an existing issue for this?

  • I have searched the existing issues

Describe the bug

When creating a new Blazor Web App template and using InteractiveServer components without global interactivity.. I cannot get the error boundary to show

In a static component with an interactive island, it does not show Error content.. in a interactive page, it doesnt show error content either..

Confused what I am doing wrong

Expected Behavior

Error boundary shows child content or error content when an unhandled exception happens in a static page or interactive page/component

Created from Blazor Web app

Steps To Reproduce

Go to the home page, and click the button to increase the count

Exceptions (if any)

warn: Microsoft.AspNetCore.Components.Server.Circuits.RemoteRenderer[100] Unhandled exception rendering component: Exception of type 'System.Exception' was thrown. System.Exception: Exception of type 'System.Exception' was thrown. at StaticErrorBoundary.Components.Pages.Counter.IncrementCount() in /Users/garrettlondon/Github/StaticErrorBoundary/Components/Pages/Counter.razor:line 29 at Microsoft.AspNetCore.Components.EventCallbackWorkItem.InvokeAsync[T](MulticastDelegate delegate, T arg) at Microsoft.AspNetCore.Components.ComponentBase.Microsoft.AspNetCore.Components.IHandleEvent.HandleEventAsync(EventCallbackWorkItem callback, Object arg) at Microsoft.AspNetCore.Components.RenderTree.Renderer.DispatchEventAsync(UInt64 eventHandlerId, EventFieldInfo fieldInfo, EventArgs eventArgs, Boolean waitForQuiescence) fail: Microsoft.AspNetCore.Components.Server.Circuits.CircuitHost[111]

Unhandled exception in circuit 'XOj9HyZGUCrv2y5bZvv5Mswe399H9w8ayWexTB5H--w'. System.Exception: Exception of type 'System.Exception' was thrown. at StaticErrorBoundary.Components.Pages.Counter.IncrementCount() in /Users/garrettlondon/Github/StaticErrorBoundary/Components/Pages/Counter.razor:line 29 at Microsoft.AspNetCore.Components.EventCallbackWorkItem.InvokeAsync[T](MulticastDelegate delegate, T arg) at Microsoft.AspNetCore.Components.ComponentBase.Microsoft.AspNetCore.Components.IHandleEvent.HandleEventAsync(EventCallbackWorkItem callback, Object arg) at Microsoft.AspNetCore.Components.RenderTree.Renderer.DispatchEventAsync(UInt64 eventHandlerId, EventFieldInfo fieldInfo, EventArgs eventArgs, Boolean waitForQuiescence)

.NET Version

8.0.302

Anything else?

No response

@ghost ghost added the area-blazor Includes: Blazor, Razor Components label Jun 23, 2024
@MackinnonBuck
Copy link
Member

Thanks for reaching out, @garrettlondon1.

It looks like the reason the error boundary doesn't show is because the error boundary is being rendered as a child component of the component throwing the exception. Since exceptions only bubble up the component hierarchy, a child error boundary component won't catch that exception.

This behavior could be changed, but we're going to backlog this for now to collect more feedback.

@MackinnonBuck MackinnonBuck added this to the Backlog milestone Jun 24, 2024
@garrettlondon1
Copy link
Author

garrettlondon1 commented Jun 24, 2024

It looks like the reason the error boundary doesn't show is because the error boundary is being rendered as a child component of the component throwing the exception.

Thanks @MackinnonBuck, but I'm confused.

Counter.razor code block has the method IncrementCount
Counter.razor markup has the onclick handler that calls IncrementCount

This interactivity lives within the same exact component, no other components are throwing an exception. Why would an error boundary not work on the Counter.razor page?

What am I missing here?

@garrettlondon1
Copy link
Author

garrettlondon1 commented Jun 24, 2024

And what is the solution to catch these unhandled exceptions without putting a try catch block in every single point of interactivity (which is crazy at scale)

@IevgeniiKunshchykov
Copy link

IevgeniiKunshchykov commented Jun 25, 2024

Hello guys,

I've got two examples:

  1. This example contains case when error is caught. There are 2 components: 1st rises error and seconds wraps 1st with ErrorBoundary

ComponentWithError.razor
image

WrappingComponent.razor
image

  1. This example contains case when error is not caugh and unhandled exception happens.
    image

I found the reson why errorboundary does not work when error happens just inside content but not in child component
There is such a class .Microsoft.AspNetCore.Components.RenderTree.Renderer.cs which contains HandleExceptionViaErrorBoundary method that recursively tries to find ErrorBoundary to handle exception. But search goes in tree by components.
image

That's why my first case works. Because WrappingComponent.razor contains ErrorBoundary
But second case has no wrapping component or that component just does not have ErrorBoundary . ErrorBoundary is located just inside that component. And that case is not covered by that loop in Renderer.cs

Seems like we are obliged always to wrap components with ErrorBoundary but cannot just wrap some content.

Any reason for that ??? Why whole tree cannot be checked ??

@garrettlondon1
Copy link
Author

garrettlondon1 commented Jun 25, 2024

@MackinnonBuck @danroth27 I see three options that Blazor forces developers into:

  • Use global interactivity with Error Boundary > not an option, cannot take advantage of SSR and per-page interactivity

  • Wrap every interactive component inside another page entirely so that Renderer.cs can see ErrorBoundary > terrible design

  • Use a try catch block to handle exceptions in every single interactive method > not scalable at all

Thank you @IevgeniiKunshchykov

@danroth27
Copy link
Member

I believe @IevgeniiKunshchykov's assessment matches are our understanding of the issue. We could make a change to make it possible for error boundaries to handle exceptions thrown by HTML event handlers. This would be a behavior change that could potentially be breaking, but it seems likely that the impact could be negligible. But before we make such a change, we want to make sure we have sufficient justification and user demand for the change. We don't want to risk breaking existing users if the beneficial impact is unclear.

@garrettlondon1 If we were to make this change, are you saying that error boundaries would then meet your error handling needs? Or are you saying in your last comment that you think Blazor needs an entirely different error handling solution?

@garrettlondon1
Copy link
Author

garrettlondon1 commented Jun 25, 2024

@danroth27 thanks for the quick reply..

ErrorBoundary is definitely the proper solution for Interactive blazor error handling

The problem is that you have to wrap every single interactive component in it because App.razor/Router is now static

  • This is fairly manageable and im fine with it..

All this story is asking for, is to make every interactive action inside of the ErrorBoundary able to bubble up the component tree correctly, and hit the ErrorBoundary properly: not throw an unhandled exception

@guardrex
Copy link
Contributor

guardrex commented Jul 11, 2024

BTW ... I think we already cover that the error boundary should be around the portion of the component hierarchy that can throw an exception. We don't say anywhere that it can directly wrap content. Mackinnon and I spent some time working on the coverage, and we're taking feedback, but I think what's being discussed here is covered.

https://learn.microsoft.com/en-us/aspnet/core/blazor/fundamentals/handle-errors?view=aspnetcore-8.0#error-boundaries

UPDATE (7/18): After discussion with @TimMurphy and @MackinnonBuck, I made improvements to our coverage to at least help clear up confusion. The updated section is live now at ...

https://learn.microsoft.com/aspnet/core/blazor/fundamentals/handle-errors#error-boundaries

@garrettlondon1
Copy link
Author

garrettlondon1 commented Jul 27, 2024

Thanks @guardrex, but the example used in the docs is a very poor example, especially when it relates to this issue.

Screenshot 2024-07-27 at 11 59 38 AM

In this photo, why would you ever make the Home component Interactive?

  • It seems like the docs are trying to avoid the fact that the ErrorBoundary is just not implemented correctly

If the Home component was not interactive (which it should NOT be), and the Counter component was interactive. Then the error boundary in the docs would not work. EVEN if the Counter is wrapped in an ErrorBoundary also

Why are we letting every individual developer figure this out painfully on their own when migrating to SSR, instead of either clarifying the docs or fixing the underlying issue?

@garrettlondon1
Copy link
Author

garrettlondon1 commented Jul 27, 2024

I would add to the docs an example where:

  • Home.razor is static, does not wrap EmbeddedCounter in an ErrorBoundary

  • EmbeddedCounter is Interactive, wrapped in an ErrorBoundary

Explain, why this does not work with @OnClick handlers
Explain, why this does work if the event originates from another subcomponent

@guardrex
Copy link
Contributor

I don't agree with your feedback (yet 😄), so I yield to @MackinnonBuck and @danroth27 to determine if further changes are needed. If not, we'll continue to take feedback and see if others find it confusing.

Most of what you're discussing is covered prior to that example. For analysis, @MackinnonBuck and @danroth27 should see the full section ...

https://learn.microsoft.com/en-us/aspnet/core/blazor/fundamentals/handle-errors?view=aspnetcore-8.0#error-boundaries

@cwoodward-taurus
Copy link

I can confirm @guardrex that we also find it confusing. We are looking for a reliable way to log Exceptions from a standalone Blazor Web Assembly Application to a Server within a Production Environment. Sources indicate that using the OnErrorAsync Method of the ErrorBoundary Component as a "global exception handler" is a way of achieving this. On closer inspection, however, it seems that Exceptions thrown from an HTML OnClick Event are not reliably handled by the ErrorBoundary Component. For other Developers looking to do something similar then it may be helpful for Microsoft to add a Note to this effect to their Documentation to hopefully minimise wasted effort going forwards. Having given this considerable thought we think the best way forwards for us, until Microsoft comes up with a better solution, is to remove the ErrorBoundary Component from our Application. @danroth27 in response to your Question to @garrettlondon1 ideally we would like a Component that provides a reliable way of handling Exceptions globally rather than adding the same code multiple times to Try...Catch blocks, which seems to be the only solution as it stands.

@guardrex
Copy link
Contributor

@cwoodward-taurus ... I'm going to enhance the coverage with additional guidance ☝️. However, I don't have product unit remarks yet on the main question that you're asking about outside of just remarking that ErrorBoundary no-ops on them ...

Exceptions thrown from an HTML OnClick Event are not reliably handled by the ErrorBoundary Component

Article remarks for that scenario will need to come from the product unit engineers here. I'll include anything else they want to say about it when I work that issue, and I should reach that issue next week after we get back from the Labor Day holiday.

@garrettlondon1
Copy link
Author

garrettlondon1 commented Aug 31, 2024

@cwoodward-taurus I want to note there is a solution. You need to create another wrapper on top of every interactive component you have..

@IevgeniiKunshchykov example calls it WrappingComponent

Page.razor (static)

  • SafeWrappingComponent.razor
  • View.razor

Its incredibly painful, but it works for all scenarios. Now that I'm thinking.. I wonder if you can make an interactive component that wraps interactive ChildContent.

Almost, wrapping ErrorBoundary itself inside of another interactive component which takes a ChildContent parameter. Will try it out.

  • Nope, Interactive components cant be serialized as ChildContent in static pages, even if its wrapped in another static component

@clarity99
Copy link

I am using blazor interactive components in MVC project and the fact that I have to wrap every component I create in another component just to catch errors is a bit frustrating, to say the least. Or is there another way to globally catch errors in such a case?

@garrettlondon1
Copy link
Author

Can we get this pulled out of the backlog?

@IevgeniiKunshchykov
Copy link

Here is one more case with unexpected behavior of errorboundary which is crucial for us.
We've solved the problem mentioned above with wrappers around our components.
Here is example:
Image

But when internal component has some state, like here I have counter with some value:
Image

And when exception is thrown and caught by errorboundary then internal component of errorboundary is just recreated and state is lost:
Image

Can this be solved somehow? Because it seems each approach of using errorboudary has some holes :(

@garrettlondon1
Copy link
Author

garrettlondon1 commented Oct 17, 2024

@danroth27 there doesn't look like there is currently a good solution to ErrorBoundary... regarding above comment.

Even if the error boundary keeps child content in the view, and does not use custom ErrorContent RenderFragment, all state will be cleared on error with WrappingComponent approach.

@codymullins
Copy link

codymullins commented Oct 18, 2024

It would be nice to be able to include the ErrorBoundary within the component I want to handle itself. Now, as others pointed out, I have to hoist up the ErrorBoundary a level instead of having a nicely encapsulated component.

Quite frankly, the inflexibility of it makes it useless for most scenarios where I would have wanted to use it.

@codymullins
Copy link

To elaborate further. Here's a real scenario where this ErrorBoundary does work:

<LoggingErrorBoundary>
    <ChildContent>
        <ServiceStatusTile Service="@svc"/>
    </ChildContent>
    <ErrorContent>
        <div class="bg-red-500 text-white p-3 rounded-lg">
            <h3 class="font-bold">An error occurred</h3>
        </div>
    </ErrorContent>
</LoggingErrorBoundary>

Fine, this renders the error content. However, it totally wipes out the component:
Image

Compare this to the happy path, where the components all render:
Image

Now, our ServiceStatusTile is a single component. What if we'd want to have an error boundary in the component itself and potentially render different parts of the component still when there's an error? We can't wrap arbitrary code, so we can't do that without these component wrappers that @garrettlondon1 mentioned. Huge pain, not maintainable.

@mkArtakMSFT mkArtakMSFT added enhancement This issue represents an ask for new feature or an enhancement to an existing one feature-blazor-error-boundary labels Oct 30, 2024
@mkArtakMSFT mkArtakMSFT modified the milestones: Backlog, .NET 10 Planning Oct 30, 2024
@rklfss
Copy link

rklfss commented Feb 12, 2025

Having issues as well.

Exceptions thrown from an HTML OnClick Event are not reliably handled by the ErrorBoundary Component

Not sure if this applies here, but had a case where an @ sign was forgotten.
While exceptions with <span onclick="SomeMethodThatThrowsEx">Click here</span> were not handled by the error boundary, exceptions with <span @onclick="SomeMethodThatThrowsEx">Click here</span> were handled.

@garrettlondon1
Copy link
Author

Having issues as well.

Exceptions thrown from an HTML OnClick Event are not reliably handled by the ErrorBoundary Component

Not sure if this applies here, but had a case where an @ sign was forgotten.
While exceptions with <span onclick="SomeMethodThatThrowsEx">Click here</span> were not handled by the error boundary, exceptions with <span @onclick="SomeMethodThatThrowsEx">Click here</span> were handled.

My guess is that fired a js onclick event instead of a blazor onclick event, so that makes sense

@garrettlondon1
Copy link
Author

Image

Blazor also doesnt catch errors in Dispose methods.. for example if a jsInterop method fails in DisposeAsync, error boundary will not catch it and whole application will break.

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 enhancement This issue represents an ask for new feature or an enhancement to an existing one feature-blazor-error-boundary triaged
Projects
None yet
Development

No branches or pull requests

10 participants