How to handle Task.Run Exception

73,618

Solution 1

When a task is run, any exceptions that it throws are retained and re-thrown when something waits for the task's result or for the task to complete.

Task.Run() returns a Task object that you can use to do that, so:

var task = Task.Run(...)

try
{
    task.Wait(); // Rethrows any exception(s).
    ...

For newer versions of C# you can use await instead ot Task.Wait():

try
{
    await Task.Run(...);
    ...

which is much neater.


For completeness, here's a compilable console application that demonstrates the use of await:

using System;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApp1
{
    class Program
    {
        static void Main()
        {
            test().Wait();
        }

        static async Task test()
        {
            try
            {
                await Task.Run(() => throwsExceptionAfterOneSecond());
            }

            catch (Exception e)
            {
                Console.WriteLine(e.Message);
            }
        }

        static void throwsExceptionAfterOneSecond()
        {
            Thread.Sleep(1000); // Sleep is for illustration only. 
            throw new InvalidOperationException("Ooops");
        }
    }
}

Solution 2

The idea of using Task.Wait will do the trick but will cause the calling thread to (as the code says) wait and therefore block until the task has finalized, which effectively makes the code synchronous instead of async.

Instead use the Task.ContinueWith option to achieve results:

Task.Run(() =>
{
   //do some work
}).ContinueWith((t) =>
{
   if (t.IsFaulted) throw t.Exception;
   if (t.IsCompleted) //optionally do some work);
});

If the task needs to continue on the UI thread, use the TaskScheduler.FromCurrentSynchronizationContext() option as parameter on continue with like so:

).ContinueWith((t) =>
{
    if (t.IsFaulted) throw t.Exception;
    if (t.IsCompleted) //optionally do some work);
}, TaskScheduler.FromCurrentSynchronizationContext());

This code will simply rethrow the aggregate exception from the task level. Off course you can also introduce some other form of exception handling here.

Solution 3

In your Outside code you only check whether starting a task does not throw exception not task's body itself. It runs asynchronously and the code which initiated it is done then.

You can use:

void Outside()
{
    try
    {
        Task.Run(() =>
        {
            int z = 0;
            int x = 1 / z;
        }).GetAwaiter().GetResult();
    }
    catch (Exception exception)
    {
        MessageBox.Show("Outside : " + exception.Message);
    }
}

Using .GetAwaiter().GetResult() waits until task ends and passes thrown exception as they are and does not wrap them in AggregateException.

Solution 4

You can just wait, and then exceptions bubble up to the current synchronization context (see answer by Matthew Watson). Or, as Menno Jongerius mentions, you can ContinueWith to keep the code asynchronous. Note that you can do so only if an exception is thrown by using the OnlyOnFaulted continuation option:

Task.Run(()=> {
    //.... some work....
})
// We could wait now, so we any exceptions are thrown, but that 
// would make the code synchronous. Instead, we continue only if 
// the task fails.
.ContinueWith(t => {
    // This is always true since we ContinueWith OnlyOnFaulted,
    // But we add the condition anyway so resharper doesn't bark.
    if (t.Exception != null)  throw t.Exception;
}, default
     , TaskContinuationOptions.OnlyOnFaulted
     , TaskScheduler.FromCurrentSynchronizationContext());

Solution 5

When option "Just My Code" is enabled, Visual Studio in some cases will break on the line that throws the exception and display an error message that says:

Exception not handled by user code.

This error is benign. You can press F5 to continue and see the exception-handling behavior that is demonstrated in these examples. To prevent Visual Studio from breaking on the first error, just disable Just My Code checkbox under Tools > Options > Debugging > General.

Share:
73,618
Mohammad Chamanpara
Author by

Mohammad Chamanpara

Updated on October 09, 2020

Comments

  • Mohammad Chamanpara
    Mohammad Chamanpara over 3 years

    I had a problem with catching the exception from Task.Run which was resolved by changing the code as follows. I'd like to know the difference between handling exceptions in these two ways :

    In the Outside method I can't catch the exception, but in the Inside method I can.

    void Outside()
    {
        try
        {
            Task.Run(() =>
            {
                int z = 0;
                int x = 1 / z;
            });
        }
        catch (Exception exception)
        {
            MessageBox.Show("Outside : " + exception.Message);
        }
    }
    
    void Inside()
    {
        Task.Run(() =>
        {
            try
            {
                int z = 0;
                int x = 1 / z;
            }
            catch (Exception exception)
            {
                MessageBox.Show("Inside : "+exception.Message);
            }
        });
    }
    
    • Sriram Sakthivel
      Sriram Sakthivel over 8 years
    • Matthew Watson
      Matthew Watson over 8 years
      That's not a duplicate if the OP is not using await... And if he's using .net 4.x he can't use await.
    • Matías Fidemraizer
      Matías Fidemraizer over 8 years
      @MatthewWatson Are you sure? await is .NET 4.x!
    • Matías Fidemraizer
      Matías Fidemraizer over 8 years
      @MatthewWatson In the other hand, does matter if he's using await or not? The answer is almost the same.
    • Matías Fidemraizer
      Matías Fidemraizer over 8 years
      @MohammadChamanpara Either if you use async/await or not, the answer is the same.
    • Matthew Watson
      Matthew Watson over 8 years
      @MatíasFidemraizer Sorry, I meant to say "4.0" not "4.x"
    • Matías Fidemraizer
      Matías Fidemraizer over 8 years
    • Matthew Watson
      Matthew Watson over 8 years
      @MatíasFidemraizer That requires VS2012 or later though. Impossible with VS2010. I should more accurately have said that await requires C#5
    • aj go
      aj go almost 2 years
      this is because when you call Task.Run(), the statement inside the Run() method is running in different process/thread which the try catch block in the main thread is not able to catch the exception
  • VMAtm
    VMAtm over 8 years
    Also task.Result throws an exception, if any.
  • DavidRR
    DavidRR almost 8 years
    Even with newer versions of C#, it isn't always possible to use await instead of Task.Wait(). From await (C# Reference): "The asynchronous method in which await is used must be modified by the async keyword."
  • Nuri YILMAZ
    Nuri YILMAZ almost 7 years
    var task = Task.Run(...) and after task.Wait() call sync, of course catched exception as all sync ops. second called never version await Task.Run(...) call async and again you cant catch exception. or I messed up... :)
  • Matthew Watson
    Matthew Watson almost 7 years
    @NuriYILMAZ I'm guessing you messed up. ;) I've appended a compilable example to illustrate using await to catch the exception. Note that the test.Wait() in Main() is to allow the async method to be called - but it's not catching the the exception- the await code is catching the exception..
  • Jürgen Steinblock
    Jürgen Steinblock over 5 years
    For Fire and Forgot Tasks I usually use .ContinueWith(t => ... log every ((AggregateException)t.Exception).Flatten().InnerExceptions ... , TaskContinuationOptions.OnlyOnFaulted).
  • Erman Akbay
    Erman Akbay about 4 years
    Not super related to the subject, but it's a bad practice to use Thread.Sleep in async code, which is what throwsExceptionAfterOneSecond is intended to be, since it's a blocking call. You should use Task.Delay() instead.
  • Matthew Watson
    Matthew Watson about 4 years
    @ErmanAkbay No, in my example throwsExceptionAfterOneSecond() is NOT intended to be async. It's supposed to be a synchronous method that takes a while to run and then throws an exception. If it was async, then we wouldn't need to use Task.Run() to run it in the first place (you would just await it), and the whole example falls to pieces. Remember, this example is intended to show how you get an exception from Task.Run(). If you made throwsExceptionAfterOneSecond() async, I feel that it would distract from what the code is trying to demonstrate.
  • Thomas LAURENT
    Thomas LAURENT about 4 years
    I don't see the point of running a task on another thread to finally wait for it to complete, your answer keeps the parallel behavior :)
  • Mike Lowery
    Mike Lowery over 3 years
    Bingo, this was it for me. Debugger was stopping on ThrowIfCancellationRequested() instead of it being caught in the calling method.
  • Craig
    Craig over 2 years
    I don't understand "throw t.Exception". What does this do? I tried surrounding the Task.Run with a try/catch but nothing was caught.
  • aj go
    aj go almost 2 years
    @thomas - i think this is used if the data it processes is not going to be displayed to the response or is not needed in other parts of current flow