Aspnetcore: Global exception handling

Created on 26 Aug 2019  ·  51Comments  ·  Source: dotnet/aspnetcore

Hello guys, I wanted to know what is the better way to catch exception at application level, to avoid put try catch in all my method and component, and centralize the exception management in one point (typically show an error notification and log the exception).

so there is a better way to do this in preview8, or something planned for the RTM ?

thanks !

Components Big Rock affected-most area-blazor enhancement severity-major

Most helpful comment

Any update on this issue ?

Currently my sever side blazor app just stopped responding to user input after an exception is thrown.

Its not a good practice to cover all lines of code with try-catch (nor to expect the developer to do so) , in case of "un-predicted" exception we should have a generic handler that allows us :

1.Choose whether to break the circuit.
2.Show our user a "friendly" GUI message or just redirect him to generic error page.

Currently after un-handleded exception the app can not notify the user on a problem and the user can only re-run the app (close the browser and goto to the app URL again) or refresh the page and the loose all the state (which is sometimes good , but the app developer should choose this).

All 51 comments

All unhandled exceptions should end up in the Logger. If you're seeing otherwise, please let us know and we'd be happy to investigate. Looking at the logs should be the way to go here.

OK, what i understand is i have to provide my own implementation of ILogger ? and its on my implementation i have to deal with the exception ? (for example show notification, write exception in a file for server side and send it to controller for client-side)
actually the only thing if found is this package https://github.com/BlazorExtensions/Logging
I don't really understand how to use it, it seem we can only log in the console with this.

But even with this extension, if there is an unhandled exception in the code, the app crash.

so if i understand, i don't have the choice to surround all of my code with try catch ?

thanks for your explanation

Any of the logger providers listed here: https://docs.microsoft.com/en-us/aspnet/core/fundamentals/logging/?view=aspnetcore-2.2#built-in-logging-providers as also community developed providers (https://github.com/aspnet/Extensions/tree/master/src/Logging#providers) should work.

But even with this extension, if there is an unhandled exception in the code, the app crash.

For server-side Blazor, an unhandled exception should disconnect the circuit, but not crash the application. If a block is meant to throw an exception you can tolerate, a try-catch seems like the most reasonable way to proceed. What do you plan on doing with unhandled exceptions if you do catch arbitrary exception?

Client side blazor does not log any exceptions. They are just printed to console. Here is the code from WebAssemblyRenderer

protected override void HandleException(Exception exception)
{
    Console.Error.WriteLine($"Unhandled exception rendering component:");
    if (exception is AggregateException aggregateException)
    {
        foreach (var innerException in aggregateException.Flatten().InnerExceptions)
        {
            Console.Error.WriteLine(innerException);
        }
    }
    else
    {
        Console.Error.WriteLine(exception);
    }
}

Only way I found that can intercept exceptions is described here
https://remibou.github.io/Exception-handling-in-Blazor/

But I would not call this exception handling...

I would like to have a global exception handler so I could write my code without catch blocks...

{
   IsLoading = true;
   await SomeAsync();
}
finally
{
   IsLoading = false;
   //force rerender after exception
    StateHasChanged();
}

If SomeAsync method throws an exeption my IsLoading varialbe is set to false but my view is not rerendered. You have to call SetStateHasChanged in finally block to force view rerender

for client side blazor, i finally write my own implementation on ILogger (inspired by the code of default implementation which log in the console) .
With this implementation, i can call a LoggerController on my server, wich a ILogger is inject too (but this time, its a NLog implementation whish is inject, so i can properly write all exception on the server)

I abandon the idea of global exception handling, which seem not be the logic of blazor, and put try/catch everywhere and inject an ILogger in all component.

Except if you think there is better to do, you can close this issue.

I think this is a missing feature.. When exception is thrown I would also like to have a centralized place to capture all unhandled exceptions and do something.. Either display it in a popup, log it somewhere, send it to the server or whatever.. without any harm to app state
Something like UnhandledException event in WPF
https://docs.microsoft.com/en-us/dotnet/api/system.appdomain.unhandledexception?redirectedfrom=MSDN&view=netframework-4.8

Isn't the ILogger already that centralized place?

@SteveSandersonMS look at my comment above
https://github.com/aspnet/AspNetCore/issues/13452#issuecomment-535227410
Exceptions (in client side blazor) are written to console using Console.Error.WriteLine, they are not logged using ILogger.. You could implement a custom TextWriter and replace Console.Error
https://remibou.github.io/Exception-handling-in-Blazor/
but you get a string representation of an exception.. Parsing that string, which inludes a stack trace, and trying to get (for example) only an exception message is cumbersome to say at least.
I would like to get an access to exception object so I can do my own logging (or what ever)

@rborosak Thanks for the clarification!

Any update on this issue ?

Currently my sever side blazor app just stopped responding to user input after an exception is thrown.

Its not a good practice to cover all lines of code with try-catch (nor to expect the developer to do so) , in case of "un-predicted" exception we should have a generic handler that allows us :

1.Choose whether to break the circuit.
2.Show our user a "friendly" GUI message or just redirect him to generic error page.

Currently after un-handleded exception the app can not notify the user on a problem and the user can only re-run the app (close the browser and goto to the app URL again) or refresh the page and the loose all the state (which is sometimes good , but the app developer should choose this).

@hagaygo Very good summary!

@SteveSandersonMS Please consider this for the 3.1.x milestone ! Please do not move this to 5.0.0

Hi,

I get this kind of error when the circuit breaks in the Console of Developer Window .

image

I dont think there anyway presently that can notify the user that an error has occurred after this in the screen and ask to refresh it without manually opening the developer window to check and refreshing the page

@SteveSandersonMS Could you clarify wheter or not the app.UseExceptionHandler() works for server-side blazor?

I have seen several cases where my custom ErrorHandler does not catch exceptions being thrown by my application. Example code

Startup.cs:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IServiceProvider serviceProvider)
{
    ...
    app.UseExceptionHandler(new ExceptionHandlerOptions { ExceptionHandler = ErrorHandler.HandleError });
    ...
}

ErrorHandler.cs:

public static async Task HandleError(HttpContext context)
{
    var error = context.Features.Get<IExceptionHandlerFeature>()?.Error;
    var message = error?.Message ?? "[EXCEPTION NOT FOUND]";        
    return;
}

An example are when my repository are throwing an exception as such:
The instance of entity type cannot be tracked because another instance with the same key value for {'Id'} is already being tracked

My MVC solution are catching all exceptions and it is using similar ErrorHandling implementations.

Moving this to 5.0 release to distill the specific asks here and see whether we can address some of these.

the biggest issue for me is error boundaries. i want to be able to use a third party component and, somehow, wrap my use of that component in error handling so that a bug in the library doesn’t kill my whole web application. right now there’s no “place to put the try catch” - the whole component tree is supervised by the blazor renderer, which will die if anything deep in the tree messes up.

+1 for this feature.
Having to put try/catch block in all methods isn't a reasonible way to go.

I personnaly used to throw exception to manage business errors. At a high level, I catch the exception and according to its type, I decide if the exception must be logged, displayed to the user and if displayed if I display the complete message (business exceptions) or just a "Something goes wrong").

Actually, the only way I found to do something like this is to create a ComponentBaseEx class which inherit to ComponentBase and override OnInitialized(Async), OnParametersSet(Async), ...
Unfortunately, even with this way to go, I have to manage manually all user's actions (delete a customer, ...)

Do you think
1) something can be done ?
2) if yes, when ?

Here's my scenario: Blazor desktop/hybrid app using Blazor server. One user logged in at a time and state persists across reloads. So, if there's an unhandled exception, there's no way to get the app back to a trustworthy state other than restarting it. We need to catch unhandled exceptions, show appropriate UX, and then shut down. Standard desktop-app pattern.

Temporary hack: we have logging set up with Serilog. Serilog has an API that lets you filter all log messages and decide whether they are emitted. We abuse this API by checking whether a log event has a CircuitId key in its properties-dictionary. If it does, that means a circuit has died - ie., there's an unhandled exception. We can get the offending exception out of the log message object that we examined. Presto - you have your hands on the unhandled exception at the very top and can do whatever you want with it.

Hello guys, like the others, i'm realy worried if you move this to the .NET 5 milestone.
After one year of work on blazor by rewriting an old silverlight app, i'm suppose to deploy my app in production in may/june (when webasssembly will be production ready).

But i definitively can't do that without a strong way to catch or at least log all unexpected errors.

Please consider make something, even not perfect, before the 5.0 release (juste a method with the exception is OK for me, at least we can log...)

Thans you very much !

Hello, I'm in the same situation that @julienGrd
Please do something to help/guide us.
Regards!

Same goes with me.

We have used Blazor Server side for all our development and migrated from
the legacy ASP.Net Web Pages.

On Tue, Mar 3, 2020 at 12:02 PM eliudgerardo notifications@github.com
wrote:

Hello, I'm in the same situation that @julienGrd
https://github.com/julienGrd
Please do something to help/guide us.
Regards!


You are receiving this because you commented.
Reply to this email directly, view it on GitHub
https://github.com/dotnet/aspnetcore/issues/13452?email_source=notifications&email_token=ANKYSDFSUQ3OTBGWQMRV5EDRFUZ27A5CNFSM4IPQWA62YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOENUJXLA#issuecomment-594058156,
or unsubscribe
https://github.com/notifications/unsubscribe-auth/ANKYSDGFCDRMZV2N4NLMOMLRFUZ27ANCNFSM4IPQWA6Q
.

--
Ankit Bansal

*Scientific Games *

The intention is that you should be able to log all uncaught exceptions using the ILogger interface. This means you can plug in your own custom logging system that does whatever you want with the exception info.

However there's still a piece of this we haven't yet implemented. This is being tracked by https://github.com/dotnet/aspnetcore/issues/19533. We'll be sure to get this done soon.

you should be able to log all uncaught exceptions using the ILogger interface

@SteveSandersonMS I think what we're after here is a much more general API, that just hands you an uncaught exception to do whatever we want _with the exception itself._ Imagine we want to examine the exception and possibly react somehow, maybe send the exception to an exception-tracking service like Raygun, or present some UI based on the contents. Without such an API, we need to misuse the ILogger interface to accomplish all these things, which is awkward. Why not just provide that much more general API, instead of forcing all possible scenarios through an ILogger implementation?

The intention is that you should be able to log all uncaught exceptions using the ILogger interface. This means you can plug in your own custom logging system that does whatever you want with the exception info.

@SteveSandersonMS By any chance do you have a reference example to do this in a Server Side Blazor app? My intention is after log an uncaught exception, show a friendly message to the user. Help will be appreciated. Regards.

@eliudgerardo Blazor Server already displays some user-facing UI when there's an unhandled error. You can customize this UI. Docs about it are at https://docs.microsoft.com/en-us/aspnet/core/blazor/handle-errors?view=aspnetcore-3.1#detailed-errors-during-development

@SteveSandersonMS I'm aware of that option you mention, but I'm afraid that in that case the connection Circuit is broken, and I don't want to wrap all my APIs calls with try/catch. So is there a way to accomplish that?

@eliudgerardo Why does it matter that the circuit is broken? Is your goal to somehow let the user continue using the circuit even after the unhandled exception?

@SteveSandersonMS We don't want the circuit to break!. If an exception occurs (in our app or somewhere in the third party library that we are using) we would like a place where we could display a dialog saying "Something went wrong {exception.Message}" and the app would continue to work. We have a partial solution to this, wrap lifecycle methods and our methods into try catch block but that is cumbersome and there is a problem with third party libraries...

@partyelite If an exception is unhandled, then that circuit is now in an unknown state. We can no longer say whether any resources inside it are still usable, whether your security logic has been executed fully, or anything else. If the circuit continued to be used, that could create undefined behavior including potential security issues, which are problematic given that this code is running on your server.

One possibility we are considering for the future is having an optional "per component unhandled exception" mechanism. If this mechanism was enabled, then the framework would do its own try/catch logic around all calls into the component's lifecycle methods, event handlers, and rendering logic. If the framework caught an exception at that level, it would consider that individual component dead (and perhaps replace it in the UI with some sort of "error" component) but would not affect execution of the rest of the circuit.

Do you think that kind of approach would meet your needs?

I certainly like the idea of having a built-in component boundary for errors.

That would be excellent. But I think we still need a method (that has an exception that occurred as an input parameter) which we could override and do something with that exception - like display a dialog with exception details or send exception details to server where it would be logged to DB or something..
I know that something similar can now be done using ILogger but we get a string as an input parameter and getting some specific data from the exception in a string format is really hard.

per-component unhandled-exception traps would be very good. ideally this would be something which can be specified externally and/or in certain components, so that we can segregate portions of the ui or functionality into error boundaries. the main necessity is to be able to do something with semantics like this:

<MyApp>
    <HandleErrors>
        <ThirdPartyComponent />
    </HandleErrors>
    <HandleErrors>
        <SomeOtherComponent />
    </HandleErrors>
</MyApp>

if either of the components fails to render, we need the rest of the application to be to continue on. it's equivalent to a rich-data desktop application in which you present some sidebar or execute some business logic function, which fails, but the app does not crash and throw away data in form fields.

in something like WPF, the use of strict view/model separation mitigates this concern; best practice is to use MVVM or MVC or whatever and have error handling around the logic that's triggered by or that updates the view. however, blazor idiomatic views are too imperative and error-prone for that to be reasonable. it's difficult to write a .xaml file which will bring down the application, but in .razor it's trivial to have an unhandled exception during rendering, whether from a data binding to an ORM-backed property or simply because you wrote

@if (mightBeNull.Value) {
    <br>
}

Isn't the ILogger already that centralized place?

Like @julienGrd I need a way to globally catch MsalUiRequiredException so I can redirect the user to a signin to request additional scopes. In MVC there is an action exception filter that handles this but Blazor has no equivalent.

Maybe ILogger is the place I am supposed to do this somehow? If so, it feels bad. My exception is an auth-related one, so it is a cross-cutting concern and I need a way to deal with it that doesn't pollute my entire application.

@kenchilada I know it feels like adding a "global" exception handler would be a quick solution to your problem, but any time you use that approach, your code completely loses track of the flow that was underway. You have no way to preserve state, and no way to provide a UX that doesn't just throw away whatever was going on at the time. In general it's much better to put a specific try/catch within the UI that might trigger it so that you can proceed without discarding all the local state.

However if you really do want to handle this at a "global" level then yes, the ILogger is a way you can do that.

The problem is that try-catch based handling is not possible with the RenderTreeBuilder model. You can write

@try
{
    <p>@{throw new Exception();}</p>
}
catch (Exception e)
{
    <p>An error occurred.</p>
}

..but it won't display "An error occurred.", it will bring down the whole circuit. You don't even get the console log and #blazor-error-ui.

@SteveSandersonMS Im totally agree with you in "theory" but im in a specific context where i don't know how to handle it, maybe you will have some answers.

I synchronise lot of code from an existing silverlight app, which i make the technical migration to blazor, but keep compatibility and share code between the two apps because the amount of work is huge and the two apps need to works side by side during a while.

so my problem is the ViewModel synchronised on the client side call wcf services like this :
````
public void MyFunction(){
wcfClient.MyFunctionCompleted += MyFunctionCompleted;
wcfClient.MyFunction();
}

private void MyFunctionCompleted(object sender, MyFunctionCompletedArgs args){
//result is here, and potential exception too
//we have to consider this code not safe so have a way to catch it
}
````

so when i call the method of my view model in my blazor view, it's useless to doing something like this
private void ButtonClick(){ try{ myViewModel.MyFunction(); } catch(Exception ex){ //i can't catch the exception in the service or happened in the callback } }

So i don't know how to handle exception return by the service, or exception happened directly in the callback, i lost the hand.

Thanks for your help !

in that case, you could actually use a TaskCompletionSource. let's say that MyFunctionCompletedArgs contains either an exception or a value of type TResult, it'd be something like this:

public Task MyFunction() 
{
    var tcs = new TaskCompletionSource<TResult>();
    wcfClient.MyFunctionCompleted += MyFunctionCompleted(tcs);
    wcfClient.MyFunction();
    return tcs.Task;
}

private EventHandler<TResult> MyFunctionCompleted(TaskCompletionSource<TResult> tcs) => (object sender, MyFunctionCompletedArgs args) =>
{
    if (args.Error != null)
    {
        tcs.SetException(args.Error);
    }
    else
    {
        tcs.SetResult(args.ReturnValue);
    }
};

then on the Blazor side, wait for the promise to be completed:

private async Task ButtonClick()
{
    try
    {
        await myViewModel.MyFunction();
    }
    catch (Exception ex)
    {
    }
}

There are many programming/software scenarios.

My conclusion from this discussion is that Microsoft (At least currently) does not want us to use Blazor for "big" apps or LOB apps since they expect us to put try/catch blocks every where.

You can maybe build a "small" apps in this way. (also , as i mentioned , its not a good practice)

But LOB apps with many use cases and 10-20 (and even more) developers in a team can not work like that.

There are of course more issues with Blazor at the moment, but this issue came up about 10-15 minutes after starting to build a POC.

Hi,

Rightly said .Hopefully Microsoft will provide an update to this later part of this year.

Ideally, It’s not possible to have error handling everywhere and when the circuit breaks the developer does not where to go. We are early adopters of Blazor and was part of the migration for one of my company projects.

There are a lot of things which needs polishing in Blazor.

Thanks & Regards,

Ankit Bansal

Scientific Games

From: Hagay Goshen notifications@github.com
Sent: Tuesday, May 26, 2020 12:21 AM
To: dotnet/aspnetcore aspnetcore@noreply.github.com
Cc: bansalankit2601 ankitbansal2601@gmail.com; Comment comment@noreply.github.com
Subject: Re: [dotnet/aspnetcore] [Blazor] Global exception handling (#13452)

There are many programming/software scenarios.

My conclusion from this discussion is that Microsoft (At least currently) does not want us to use Blazor for "big" apps or LOB apps since they expect us to put try/catch blocks every where.

You can maybe build a "small" apps in this way. (also , as i mentioned , its not a good practice)

But LOB apps with many use cases and 10-20 (and even more) developers in a team can not work like that.

There are of course more issues with Blazor at the moment, but this issue came up about 10-15 minutes after starting to build a POC.


You are receiving this because you commented.
Reply to this email directly, view it on GitHub https://github.com/dotnet/aspnetcore/issues/13452#issuecomment-633799650 , or unsubscribe https://github.com/notifications/unsubscribe-auth/ANKYSDATEUMBPN43PYQU2ZDRTM7T3ANCNFSM4IPQWA6Q . https://github.com/notifications/beacon/ANKYSDD3JBBDUMUWMZ5NQXDRTM7T3A5CNFSM4IPQWA62YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEXDQHYQ.gif

There are many programming/software scenarios.

My conclusion from this discussion is that Microsoft (At least currently) does not want us to use Blazor for "big" apps or LOB apps since they expect us to put try/catch blocks every where.

You can maybe build a "small" apps in this way. (also , as i mentioned , its not a good practice)

But LOB apps with many use cases and 10-20 (and even more) developers in a team can not work like that.

There are of course more issues with Blazor at the moment, but this issue came up about 10-15 minutes after starting to build a POC.

This is where we landed as well. We are using it for a backend admin interface but it is much too novel at the moment to be used for our customer-facing application.

@gulbanana thanks for the tip ! Not sure i will keep compatibility with the silverlight app with this kind of code, but i have to try (even if i prefere a global exception handling solution)

in that case, you could actually use a TaskCompletionSource. let's say that MyFunctionCompletedArgs contains either an exception or a value of type TResult, it'd be something like this:

public Task MyFunction() 
{
    var tcs = new TaskCompletionSource<TResult>();
    wcfClient.MyFunctionCompleted += MyFunctionCompleted(tcs);
    wcfClient.MyFunction();
    return tcs.Task;
}

private EventHandler<TResult> MyFunctionCompleted(TaskCompletionSource<TResult> tcs) => (object sender, MyFunctionCompletedArgs args) =>
{
    if (args.Error != null)
    {
        tcs.SetException(args.Error);
    }
    else
    {
        tcs.SetResult(args.ReturnValue);
    }
};

then on the Blazor side, wait for the promise to be completed:

private async Task ButtonClick()
{
    try
    {
        await myViewModel.MyFunction();
    }
    catch (Exception ex)
    {
    }
}

A bit confused here... If global exception handling isn't doable, then why is there a product out there
for blazor that claims that "All uncaught exceptions are automatically logged"?:
https://docs.elmah.io/logging-to-elmah-io-from-blazor/

@rono1 You can now hook into the logging pipeline and log exceptions -- that doesn't mean you've "handled" the exception, though, just that you can globally log them. I'm thankful for the logging, though!

We've moved this issue to the Backlog milestone. This means that it is not going to be worked on for the coming release. We will reassess the backlog following the current release and consider this item at that time. To learn more about our issue management process and to have better expectation regarding different types of issues you can read our Triage Process.

One possibility we are considering for the future is having an optional "per component unhandled exception" mechanism. If this mechanism was enabled, then the framework would do its own try/catch logic around all calls into the component's lifecycle methods, event handlers, and rendering logic. If the framework caught an exception at that level, it would consider that individual component dead (and perhaps replace it in the UI with some sort of "error" component) but would not affect execution of the rest of the circuit.

Do you think that kind of approach would meet your needs?

@SteveSandersonMS Is this being considered for implementation? It will result in a better user experience and more robust software.

+1 on this. We're really supposed to wrap every single event in a try/catch? I just went down a rabbit hole of trying to work around this with some base components to manage state because of how painful this is.

+1 also on this. The try/catch approach really isn't an option for larger solutions with multiple developers.

I adressed this in my client side blazor app by using a custom logging provider:
https://github.com/markusrt/NRZMyk/blob/master/NRZMyk.Client/ReloadOnCriticalErrorLogProvider.cs

It seems a bit weird to use a logger as global exception handler but at I did not find any cleaner approach as of now 😞

The Blazor team will definitively need to do something about this
request/issue.

On Sun, 4 Oct 2020 at 15:24, Markus Reinhardt notifications@github.com
wrote:

>
>
>

I adressed this in my client side blazor app by using a custom logging
provider:

https://github.com/markusrt/NRZMyk/blob/master/NRZMyk.Client/ReloadOnCriticalErrorLogProvider.cs

It seems a bit weird to use a logger as global exception handler but at I
did not find any cleaner approach as of now 😞


You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub
https://github.com/dotnet/aspnetcore/issues/13452#issuecomment-703255415,
or unsubscribe
https://github.com/notifications/unsubscribe-auth/AEVARXEEHAOU532C26QCGXLSJBZRRANCNFSM4IPQWA6Q
.

Thanks for contacting us.
We're moving this issue to the Next sprint planning milestone for future evaluation / consideration. We will evaluate the request when we are planning the work for the next milestone. To learn more about what to expect next and how this issue will be handled you can read more about our triage process here.

For anyone looking for a client-side workaround:

function reportError(error) {
  if (DotNet) {
    DotNet.invokeMethodAsync('MyApp.Client', 'NotifyError', error);
  }
}

var exLog = console.error;
console.error = function (msg) {
  exLog.apply(console, arguments);
  reportError(msg);
}

window.addEventListener("unhandledrejection", function (promiseRejectionEvent) {
  reportError(promiseRejectionEvent.reason.message);
});
  public partial class AppLayout
  {
    [JSInvokable("NotifyError")]
    public static void NotifyError(string error)
    {
      //Handle errors
    }
  }

Browser support

This is a critically important feature for a production application and I wouldn't feel comfortable releasing an application where I couldn't control the application's behavior in the event of an unhandled error, or at least be able to reliably log it. Fortunately, I'm sure there's enough support for this in JavaScript we can leverage, however it's a big enough gap to make me question whether this technology's really mature enough to be adopted in serious applications.

I hope this a global error handler is prioritized in future releases.

Was this page helpful?
0 / 5 - 0 ratings