Bug 36560 - Unhandled exceptions outside the main execution context are ignored
Summary: Unhandled exceptions outside the main execution context are ignored
Alias: None
Product: Runtime
Classification: Mono
Component: General ()
Version: unspecified
Hardware: All All
: --- normal
Target Milestone: ---
Assignee: Alex Rønne Petersen
Depends on:
Reported: 2015-12-03 20:49 UTC by Matthew Orlando
Modified: 2017-01-10 11:06 UTC (History)
5 users (show)

Is this bug a regression?: ---
Last known good build:

Notice (2018-05-24): bugzilla.xamarin.com is now in read-only mode.

Please join us on Visual Studio Developer Community and in the Xamarin and Mono organizations on GitHub to continue tracking issues. Bugzilla will remain available for reference in read-only mode. We will continue to work on open Bugzilla bugs, copy them to the new locations as needed for follow-up, and add the new items under Related Links.

Our sincere thanks to everyone who has contributed on this bug tracker over the years. Thanks also for your understanding as we make these adjustments and improvements for the future.

Please create a new report on GitHub or Developer Community with your current version information, steps to reproduce, and relevant error messages or log files if you are hitting an issue that looks similar to this resolved bug and you do not yet see a matching new report.

Related Links:

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.)