Search code examples
c#.netexceptiontask-parallel-libraryasync-await

ContinueWith TaskContinuationOptions.OnlyOnFaulted does not seem to catch an exception thrown from a started task


I'm trying to catch an exception thrown from a task method using ContinueWith and OnlyOnFaulted like below. However I get an unhandled exception while I try to run this code.

I'd like the task to run to completion since I have handled the exception already. But Task.Wait() runs into AggregateException.

var taskAction = new Action(() =>
{
    Thread.Sleep(1000); 
    Console.WriteLine("Task Waited for a sec");
    throw (new Exception("throwing for example"));
});
Task t = Task.Factory.StartNew(taskAction);
t.ContinueWith(x => Console.WriteLine("In the on Faulted continue with code. Catched exception from the task."+  t.Exception), TaskContinuationOptions.OnlyOnFaulted);
Console.WriteLine("Main thread waiting for 4 sec");
Thread.Sleep(4000);
Console.WriteLine("Wait of 4 secs complete..checking if task is completed?");
Console.WriteLine("Task State: " + t.Status);
t.Wait();    

If I handle the exception in the task method like below, everything will go normal as I expect. Task Runs Into Completion, Exception gets logged and Wait succeeds also.

var taskAction = new Action(() =>
{
    try
    {
        Thread.Sleep(1000); 
        Console.WriteLine("Task Waited for a sec"); 
        throw (new Exception("throwing for example"));
    }
    catch (Exception ex)
    {
        Console.WriteLine("Catching the exception in the Action catch block only");
    }
});
Task t = Task.Factory.StartNew(taskAction);
t.ContinueWith(x=> Console.WriteLine("In the on Faulted continue with code. Catched exception from the task."+  t.Exception), TaskContinuationOptions.OnlyOnFaulted);
Console.WriteLine("Main thread waiting for 4 sec");
Thread.Sleep(4000);
Console.WriteLine("Wait of 4 secs complete..checking if task is completed?");
Console.WriteLine("Task State: " + t.Status);
t.Wait();    

My question is: Am I using the OnlyOnFaulted correctly or is it always better to handle the exception in the task method itself? I would like the main thread to continue even if task runs into exception. Also, I want to log that exception from task method.

Note: I have to wait for the task method to complete before going further (with or without errors).

To summarize(my understanding so far)

If exception from Task is handled i.e. if wait or await catches the exception then the exception would be propagated to continuedtask onfaulted. Exception can be caught even in the task method and consumed\handled.

try
{ 
   t.wait();
}
catch(Exception e)
{
   LogError(e);
}

In above case before LogError gets called, continued task associated with the main task's onfaulted gets executed.


Solution

  • First of all, you aren't using OnlyOnFaulted correctly. When you use ContinueWith on a task, you don't really change that task, you get back a task continuation (which, in your case, you disregard).

    If the original task faulted (i.e., had an exception thrown inside it) it would stay faulted (so calling Wait() on it would always rethrow the exception). The continuation, however, would run after the task is faulted and handle the exception.

    That means that in your code, you do handle the exception, but you're also rethrowing it with Wait(). The correct code should look like this:

    Task originalTask = Task.Run(() => throw new Exception());
    Task continuationTask = originalTask.ContinueWith(
        t => Console.WriteLine(t.Exception), 
        TaskContinuationOptions.OnlyOnFaulted);
    
    continuationTask.Wait();
    // Both tasks completed. No exception rethrown
    

    Now, as Yuval Itzchakov pointed out, you can handle the exception wherever you want, but it would be better if you were utilizing async-await to wait asynchronously if you can (you can't in Main) instead of blocking with Wait():

    try
    {
        await originalTask;
    }
    catch (Exception e)
    {
        // handle exception
    }