I've just learned that .NET 4.5 introduced a change to how exceptions inside a Task are handled. Namely, they are quietly suppressed.

The official reasoning for why this was done appears to be "we wanted to be more friendly to inexperienced developers":

In .NET 4.5, Tasks have significantly more prominence than they did in .NET 4, as they’re baked in to the C# and Visual Basic languages as part of the new async features supported by the languages. This in effect moves Tasks out of the domain of experienced developers into the realm of everyone. As a result, it also leads to a new set of tradeoffs about how strict to be around exception handling.


I've learned to trust that many of the decisions in .NET were made by people who really know what they're doing, and there is usually a very good reason behind things they decide. But this one escapes me.

If I were designing my own async task library, what is the advantage of swallowing exceptions that the developers of the Framework saw that I'm not seeing?

  • 4
    +1. Good question, given the fact that not propagating exceptions you can't handle is a bad practice even aside asynchronous context. Also, the argument about inexperienced developers is pretty lame, IMHO. What would be more awkward for beginners than a piece of code which not only doesn't work, but also throws no exceptions whatsoever? – Arseni Mourzenko Apr 25 '13 at 10:46
  • @MainMa Exactly my thoughts, but I've been wrong before (when it turned out they did, in fact, have a very good but un-obvious reason), so I thought I'd ask. – Roman Starkov Apr 25 '13 at 10:49
  • 2
    Wow, I'm glad you posted this just because it's the first I've heard of it; and it definitely violates POLA. You're right that those guys really know what they're doing though, so I'm sincerely curious what reasoning might be behind this... Wonder if it's got a more technical reason based on the implementation of Async and how exceptions would propagate as bottom being passed to a coroutine.. – Jimmy Hoffa Apr 25 '13 at 11:28

For what it's worth, the document you linked to gives an example case as justification:

Task op1 = FooAsync(); 
Task op2 = BarAsync(); 
await op1; 
await op2;

In this code, the developer is launching two asynchronous operations to run in parallel, and is then asynchronously waiting for each using the new await language feature...[C]onsider what will happen if both op1 and op2 fault. Awaiting op1 will propagate op1’s exception, and therefore op2 will never be awaited. As a result, op2’s exception will not be observed, and the process would eventually crash.

To make it easier for developers to write asynchronous code based on Tasks, .NET 4.5 changes the default exception behavior for unobserved exceptions. While unobserved exceptions will still cause the UnobservedTaskException event to be raised (not doing so would be a breaking change), the process will not crash by default. Rather, the exception will end up getting eaten after the event is raised, regardless of whether an event handler observes the exception.

I'm not convinced by this. It removes the possibility of an unambiguous, but hard to trace error (mysterious program crash that might occurs long after the actual error), but replaces it with the possibility of a completely silent error--which might become an equally hard to trace problem later on in your program. That seems like a dubious choice to me.

The behavior is configurable--but of course, 99% of developers are just going to use the default behavior, never thinking about this issue. So what they selected as the default is a big deal.

  • But you do get a normal exception for op1, right? If that one remains unhandled, it will bring down the process, right? – Timwi Apr 25 '13 at 12:49
  • 1
    @Timwi, as I understand it, neither op1's exception nor op2's exception would bring down the program. The advantage is that you have the opportunity to observe both. But if you don't, they will both be swallowed. I could be wrong, though. – user82096 Apr 25 '13 at 13:26
  • I'm really surprised that they find it so important to let you "observe" both. If you want to "observe" them, you catch them and report their occurrence via the task result!... Agree with your reasoning, +1 – Roman Starkov Apr 30 '13 at 12:59

TL;DR - No, you shouldn't just ignore exceptions quietly.

Let's look at the assumptions.

  • Your blind faith

I've learned to trust that many of the decisions in .NET were made by people who really know what they're doing, and there is usually a very good reason behind things they decide. But this one escapes me.

MS has some really brilliant people on staff, no doubt. They also have a number of managers and executives who are ... clueless, and who consistently make bad decisions. As much as we'd like to believe the fairy tale of the technically savvy scientist driving the product development, the reality is far more grim.

My point being, if your instinct tells you something may be wrong then there's a good chance (and past precedent) that it is wrong.

  • That this is a technical issue

How to handle exceptions in this case is a human factors decision, not a technical decision. Loosely, an exception is an error. Presentation of the error, even if poorly formatted, provides critical information to the end user. Namely, something went wrong.

Consider this hypothetical conversation between and end user and a developer.

User: The app is broken.
Dev: What's broken?
User: I dunno, I click on the button and nothing happens.
Dev: What do you mean by nothing happens?
User: Look, I click, I wait, and I wait, and I wait, and nothing... it's obviously broken.

Our poor, fortunately hypothetical Developer now has to figure out what went wrong in the chain of events. Where was it?
Event handler notification -> Routine to handle the event -> Method triggered by handler -> beginning of asynchronous call -> the 7 layers of OSI networking -> physical transmission -> back up the 7 layers of OSI networking -> receiving service -> method called by service -> ... -> return reply sent by service -> .... -> asynch receipt -> processing of asynch reply -> ...

And please note that I've glossed over several potential error paths there.

After addressing some of the underlying assumptions, I think it becomes more clear why quietly suppressing exceptions is a bad idea. An exception and associated error message is a key indicator to the users that something went wrong. Even if the message is meaningless to the end user, the embedded information can be useful to the Developer in understanding what went wrong. If they're lucky, the message will even lead towards the solution.

I think part of the problem here is that an incorrectly handled exception will crash your application. By suppressing the exceptions, the application won't crash. There is some validity to this though since the point of async is to allow the application to keep working while data is being retrieved. It's not that much of a logical extension to say that if you could keep operating while waiting on the results then a failure in retrieval shouldn't crash the application either - the async call is implicitly declared as not important enough to end the application.

So it's a solution, but it's solving the wrong problem. It doesn't take that much effort to provide a wrapper to the exception handling so that the application can remain running. The exception is caught, error message thrown to the screen or logged, and the application is allowed to keep on running. Suppressing the exception implies that ignoring errors is going to be A-OK and that your testing will make sure exceptions never escape to the field anyway.

So my end assessment is that this is a half-baked attempt at resolving an issue created by pushing people into an asynchronous model when they weren't ready to shift their thinking to that approach.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.