Home > Blockchain >  Unable to get the exceptions in try catch
Unable to get the exceptions in try catch

Time:02-11

I have a worker service in .net core 3.1 in my Program.cs i have the below codes

public static void Main(string[] args)
{
    try
    {
        CreateHostBuilder(args).Build().Run();
    }
    catch(Exception ex)
    {
        Handler(ex);
    }
}

And also a worker class as below

public class Worker : BackgroundService
{ 
  protected override async Task ExecuteAsync(CancellationToken Token)
  {
    
    do
    { 
     if(condition)
     {
      await _test.GetData();
     }
     await Task.Delay(500,Token);
    }
    while (!Token.IsCancellationRequested);

  }
 }

If Task.Delay executes first and then any exception occurs in the GetData() method on the second iteration, it is not being captured by the try catch in my Main method. How can i get these exception to be caught in my main method?

CodePudding user response:

That's the expected behavior.

Your catch block in the Program.cs file will only handle exceptions thrown when building and starting the .NET core generic host. That's all. The catch block is not expected to handle exceptions thrown by the registered hosted services during their execution.

If you check the source code for the BackgroundService base class you will understand why exceptions thrown by a background service are lost.

At this line you see that your implementation of ExecuteAsync is invoked and the resulting task is stored inside the _executeTask field.

Your override of ExecuteAsync is an async function. When there is an unhandled exception inside of an async funcition, the exception is captured by the runtime and placed on the returned Task. The exception will be thrown (with its original stacktrace preserved) when the Task is awaited, or an api like Task.Wait() is called.
Notice that the _executeTask Task is not awaited and that the executing thread is not trying to synchronously wait for the task completion by calling the Task.Wait() method. So, there is no chance to observe exceptions coming from that Task.

There is only one possibility to observe an exception coming from your implementation of ExecuteAsync. If an exception occurs before the very first await instruction, then the method execution completes synchronously and the returned Task will have the IsCompleted property set to true and its Status will be Faulted. In that case, the _executeTask task will be returned to the calling code (see this line).

The calling code, which is basically the code starting the .NET core generic host, will get back a faulted task and will detect the error occurred while starting the background service by awaiting the task itself. At that point, the exception coming from the ExecuteAsync method will be rethrown and the catch block in your Program.cs will handle the exception.

If any exception occurs inside of ExecuteAsync after the first await instruction, there is no way for the .NET core generic host to observe the exception itself. Those exceptions will result in silent failures. You can find more details on this by reading this great blog post.

As pointed out by a comment to your question, this behavior has been changed in .NET 6, as you can read here. To put it briefly, the default behaviour in .NET 6 is logging any unhandled exception coming from a background service and stopping the Host, so that you are aware of the problem and you have a chance to fix your code and investigate why it is throwing exceptions.

CodePudding user response:

Since ExcecuteAsync is async, any exception will be wrapped in the resulting Task. If you do not await this task, the exception will not be thrown.

In the provided code we can not see, whether or not this call is awaited but I guess somewhere the exception gets wrapped in a task that is not awaited.

  • Related