Search code examples
c#continuewith

Exceptions are not caught with ContinueWith?


Using Visual Studio 2015, targeting FW 4 (testing unobservable exceptions under FW 4):

enter image description here

I'm expecting this code:

static void Main(string[] args)
{

    try
    {
        Task.Factory.StartNew(() => Console.WriteLine(1))
        .ContinueWith(t => Thread.Sleep(1000))
        .ContinueWith(t => Console.WriteLine(2))
        .ContinueWith(t => Thread.Sleep(1000))
        .ContinueWith(t => { throw new Exception("aaaa"); })
        .ContinueWith(t => Console.WriteLine(3));
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex);

    }
    GC.Collect();
    GC.Collect();
    Console.ReadLine();
}

To show me the exception.

I know I can see it via T.Wait() or in the last task with t.Exception - but why am I not seeing the exception here?

I know that the exception handling mechanism was changed in 4.5 and in order to get the old mechanism I should add:

 <ThrowUnobservedTaskExceptions enabled="true"/>

Which I did:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
    <startup> 
        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/>
    </startup>
  <runtime>
    <ThrowUnobservedTaskExceptions enabled="true"/>
  </runtime>
</configuration>

But still the result is:

enter image description here

Question:

Why don't I see the exception?

Worth a mention that I do see the exception in debug mode:

enter image description here


Solution

  • You cannot expect this code to throw an exception because all that the statements in the try clause do is describe a continuation pattern. The StartNew and ContinueWith methods do not thrown an exception. So the code execution has long left this try statement when the exception in a background task is thrown. When the exception is thrown around 2 seconds later after the program has started the execution is halted on the Console.Readline statement.

    As you have already discovered you will need to wait for the tasks to finish before being able to access the exception or inside the continuation itself.

    Now the reason why your application doesn't die with an unobserved exception is because Garbage Collection hasn't occurred. The unobserved exception will tear down your application domain when the Task is garbage collected. But by the time you are forcing GC, the exception hasn't yet been thrown neither the task has finished so it is not eligible for Garbage Collection.

    Make sure that you place your GC.Collect calls after the Console.ReadLine method followed by yet another Console.ReadLine:

    Task.Factory.StartNew(() => Console.WriteLine(1))
        .ContinueWith(t => Thread.Sleep(1000))
        .ContinueWith(t => Console.WriteLine(2))
        .ContinueWith(t => Thread.Sleep(1000))
        .ContinueWith(t => { throw new Exception("aaaa"); })
        .ContinueWith(t => Console.WriteLine(3));
    
    Console.ReadLine();
    GC.Collect();
    GC.Collect();
    Console.ReadLine();
    

    Obviously the following config switch should be present:

    <ThrowUnobservedTaskExceptions enabled="true"/>