How to handle Task.Run Exception
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.
Mohammad Chamanpara
Updated on October 09, 2020Comments
-
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 theInside
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 over 8 years
-
Matthew Watson over 8 yearsThat's not a duplicate if the OP is not using
await
... And if he's using .net 4.x he can't useawait
. -
Matías Fidemraizer over 8 years@MatthewWatson Are you sure?
await
is .NET 4.x! -
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 over 8 years@MohammadChamanpara Either if you use async/await or not, the answer is the same.
-
Matthew Watson over 8 years@MatíasFidemraizer Sorry, I meant to say "4.0" not "4.x"
-
Matías Fidemraizer over 8 years
-
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 almost 2 yearsthis 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 over 8 yearsAlso
task.Result
throws an exception, if any. -
DavidRR almost 8 yearsEven with newer versions of C#, it isn't always possible to use
await
instead ofTask.Wait()
. From await (C# Reference): "The asynchronous method in which await is used must be modified by the async keyword." -
Nuri YILMAZ almost 7 yearsvar 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 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 thetest.Wait()
inMain()
is to allow the async method to be called - but it's not catching the the exception- theawait
code is catching the exception.. -
Jürgen Steinblock over 5 yearsFor Fire and Forgot Tasks I usually use
.ContinueWith(t => ... log every ((AggregateException)t.Exception).Flatten().InnerExceptions ... , TaskContinuationOptions.OnlyOnFaulted)
. -
Erman Akbay about 4 yearsNot 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 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 useTask.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 fromTask.Run()
. If you madethrowsExceptionAfterOneSecond()
async, I feel that it would distract from what the code is trying to demonstrate. -
Thomas LAURENT about 4 yearsI 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 over 3 yearsBingo, this was it for me. Debugger was stopping on
ThrowIfCancellationRequested()
instead of it being caught in the calling method. -
Craig over 2 yearsI 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 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