7

I am new to C# and I find exceptions a bit confusing... I have a web app with the following code:

try
{
    //do something
}
catch (TimeoutException t)
{
    Console.WriteLine(t);
}
catch (TaskCanceledException tc)
{
    Console.WriteLine(tc);
}
catch (Exception e)
{
    Console.WriteLine(e);
}

When I debug the code, it throws the e Exception, the most general one and when I hover over the exception info it turns out that it's the TaskCanceledException. Why isnt the TaskCanceledException caught? If the exception was a TimeoutException would it catch the TimeoutException or would it also catch the Exception? Why is that?

Yuval Itzchakov
  • 146,575
  • 32
  • 257
  • 321
Marta
  • 1,132
  • 1
  • 10
  • 26
  • 3
    Are you sure it isn't an `AggregationException` thrown from a `Task` object? show us the throwing call – Yuval Itzchakov Jul 21 '14 at 13:36
  • ...yes, it's the System.AggregateException and the InnerException is the System.Threading.Task.TaskCanceledException. – Marta Jul 21 '14 at 13:44

1 Answers1

6

When catching an Exception, you have to make sure it is exactly the exception being thrown. When using Task.Run or Task.Factory.Startnew and generally involving an exception being thrown from a Task (unless the task is being awaited with the await keyword), the outer exception you're dealing with is an AggregateException, because a Task unit may have child tasks which may also throw exceptions.

From Exception Handling (Task Parallel Library):

Unhandled exceptions that are thrown by user code that is running inside a task are propagated back to the joining thread, except in certain scenarios that are described later in this topic. Exceptions are propagated when you use one of the static or instance Task.Wait or Task.Wait methods, and you handle them by enclosing the call in a try-catch statement. If a task is the parent of attached child tasks, or if you are waiting on multiple tasks, then multiple exceptions could be thrown. To propagate all the exceptions back to the calling thread, the Task infrastructure wraps them in an AggregateException instance. The AggregateException has an InnerExceptions property that can be enumerated to examine all the original exceptions that were thrown, and handle (or not handle) each one individually. Even if only one exception is thrown, it is still wrapped in an AggregateException.

So, in order to deal with that, you have to catch AggregateException:

try
{
    //do something
}
catch (TimeoutException t)
{
    Console.WriteLine(t);
}
catch (TaskCanceledException tc)
{
    Console.WriteLine(tc);
}
catch (AggregateException ae)
{
   // This may contain multiple exceptions, which you can iterate with a foreach
   foreach (var exception in ae.InnerExceptions)
   {
       Console.WriteLine(exception.Message);
   }
}
catch (Exception e)
{
    Console.WriteLine(e);
}
Yuval Itzchakov
  • 146,575
  • 32
  • 257
  • 321
  • Ok. Now my app should timeout, but instead of a TimeoutException I get this AggregateException with TaskCanceledException inside. How do I get the program to catch the TimeoutException? – Marta Jul 21 '14 at 14:26
  • basically is it possible for a TimeoutException to be thrown instead of TaskCanceledException? – Marta Jul 21 '14 at 15:02
  • No, it isn't possible. An `AggregateExcception` is thrown from a `Task` because it may also contain children tasks. Its simply a wrapper. – Yuval Itzchakov Jul 21 '14 at 15:22