Bug 36560

Summary: Unhandled exceptions outside the main execution context are ignored
Product: [Mono] Runtime Reporter: Matthew Orlando <matthew>
Component: GeneralAssignee: Alex Rønne Petersen <alexrp>
Severity: normal CC: aleksey, alexrp, mono-bugs+mono, mono-bugs+runtime, stephan
Priority: ---    
Version: unspecified   
Target Milestone: ---   
Hardware: All   
OS: All   
Tags: Is this bug a regression?: ---
Last known good build:

Description Matthew Orlando 2015-12-03 20:49:38 UTC
Unhandled exceptions are supposed to terminate the program. However, if you have an unhandled exception off the main thread, or in a continuation of some async task even on the main thread, the exception will be completely lost. It's not reported in the debug output, it doesn't terminate the app, it just silently fails leading to all manner of confusing behavior.

This happens on all Xamarin platforms (iOS, Android, OSX, Forms) hence my choice of Runtime as the product. 

Reproduce by adding this code pretty much anywhere in the main thread of your app:

Basic thread exception:

    Task.Run(() =>
        throw new Exception("deadbeef");

Continuation exception:

    Task.Run(() =>
        throw new Exception("deadbeef");
    }).ContinueWith(prevTask =>
        if (prevTask.IsFaulted)
            throw prevTask.Exception;
    }, TaskScheduler.FromCurrentSynchronizationContext());

Compare behavior with the same code added to a genuine .Net app (e.g. Windows Forms or WPF)
Comment 1 Matthew Orlando 2015-12-03 21:09:01 UTC
.Net console apps built with Xamarin Studio also lose the exception (only the basic example works; console thread is not a valid synchronization context)
Comment 2 Matthew Orlando 2015-12-03 21:42:59 UTC
On iOS, if I call InvokeOnMainThread inside the continuation instead of using TaskScheduler, the exception propagates as expected.
Comment 3 Matthew Orlando 2015-12-09 19:02:24 UTC
This is making it incredibly frustrating to test new asynchronous code. I add new things that look like they should work, set a breakpoint where I want to check my assumptions, but that breakpoint is never reached, and the app fails silently in ways I may not notice. I basically have to step through every piece of new asynchronous code I add, searching for phantoms.
Comment 4 Aleksey Kliger 2015-12-21 16:52:50 UTC
Matthew, thank you for reporting this issue.

It's not clear to me whether the behavior you describe (exception is not observed on 
the main thread) happens if you call any of the Wait() methods on the created task?  

(Console apps running with Mono and or from the Command Prompt on Windows behave identically here: if the main thread exits before waiting the exception from the task is silently dropped.)
Comment 5 Alex Rønne Petersen 2015-12-22 09:15:01 UTC

I believe this is, for better or worse, working as expected. .NET 4.5 changed the default so that unobserved task exceptions don't terminate the process. If you Wait () on the task, you will get the exception as expected.

More details here: http://blogs.msdn.com/b/pfxteam/archive/2011/09/28/task-exception-handling-in-net-4-5.aspx

However, we do not support <ThrowUnobservedTaskExceptions /> at the moment, which is unfortunate as it's the config flag that gives the 'old' behavior. I will have a look at this.
Comment 6 Alex Rønne Petersen 2016-01-13 03:58:57 UTC
I've implemented support for the config element mentioned earlier. A pull request is currently pending review: https://github.com/mono/mono/pull/2424
Comment 7 Matthew Orlando 2016-01-13 17:13:52 UTC
Hi Alexsey & Alex,

Thanks for the clarification. I do see that I was wrong about the way WPF & Windows Forms behave in this respect. It was only in debug builds where it crashed with an unhandled exception. I'm guessing MS enables the ThrowUnobservedTaskExceptions flag for debug builds much like they add bounds checking for STL containers.

I've converted all my code to use await instead of ContinueWith and haven't had any further issues.


Comment 8 Matthew Orlando 2016-01-13 17:15:12 UTC
(sorry I spelled your name wrong, Aleksey!)
Comment 9 Alex Rønne Petersen 2016-01-14 02:14:40 UTC
That's entirely possible. I don't think that behavior is documented anywhere, though. I'll investigate at some point.

(BTW, the PR above has been merged, so you can now use this configuration element if you wish.)