8

Can anyone explain what exactly Microsoft means on the below linked page when it says:

"Do not catch an exception unless you can handle it and leave the application in a known state."

https://msdn.microsoft.com/en-us/library/ms173160.aspx

3
  • 19
    How much of the sentence do you understand? Do you know what an exception is? Do you know what it means to catch an exception?
    – JacquesB
    Aug 30, 2016 at 14:56
  • 8
    You don't catch exceptions just because they are thrown, you catch them because you have some specific and useful recovery you want to undertake. This is the beauty of exceptions, you handle them only at a place in the code where you can do some useful recovery, which is often far away from where they are generated. Under many circumstances, the code in between doesn't have to deal with them at all.
    – Erik Eidt
    Aug 30, 2016 at 16:08
  • 1
    Eric Lippert wrote a fantastic article called Vexing Exceptions a few years ago. It's a quick read and lays out a great mindset for approaching your error handling.
    – DanK
    Aug 30, 2016 at 18:35

5 Answers 5

20
  • It means catching an exception to just write to a log or worse, do nothing at all, leaves the app in an unknown state.
  • Maybe something that shouldn't be null will be, or something that should have been saved wouldn't.
  • You are just sweeping the dirt under the rug.
  • If you are going to catch an exception do it because you can do something about it so the app can continue its execution in a known (and expected) state.
  • Catch an exception when you actually can do something about the problem that caused it or at least leave the app in a consistent state.

Else...

throw it along... someone up the call stack will have to handle it.

If no-one up the calling stack handles it, it'll just bubble up until it reaches the UI an at least the user will know there's something wrong.

But, as everything in this programming world, there are exceptions (no pun intended):

Sometimes in a finally block you try to close a database resource that could or could have not been initialized. In that case there's a probability of calling close() on a null object. In that case silently catching NullPointerException is OK. You cannot handle it anyway. Although, as as @marco-borchert, checking for null would be cleaner.

Another possible exception to the rule is, as @marco-borchert also pointed out, to log the error and re-throw. Maybe you have info in that scope that won't be available up the call stack and it would be interesting to log even if you will not handle the exception at that level.

13
  • 8
    catching an exception to just write to a log There's also the possiblity of log and rethrow for cases where you have to expect your exception to be caught and ignored (violating the rule in the OP) higher up the call hierarchy outside of your control. Aug 30, 2016 at 15:02
  • 1
    "Sometimes in a finally block you try to close a database resource that could or could have not been initialized." Isn't this what the 'using' clause is for?
    – JimmyJames
    Aug 30, 2016 at 16:30
  • 2
    ...In that case silently catching NullPointerException is OK. You cannot handle it anyway. In that case, I would argue that the empty catch block is handling the exception. It's just a degenerate case because there isn't any actual work that the handler needs to do. Aug 30, 2016 at 16:56
  • 1
    @RobertHarvey Not a technical term. It's similar to pass along, to relay. Aug 30, 2016 at 17:28
  • 2
    "Just write to a log" I find essential. In early development, it helps me catch the "BoneHeaded" exceptions Eric Lippert refers to. Being human, there always seem to be some of these. After I think I've caught them all, the fact that I do have a log may still show me things I did not consider earlier. Whether or not the exception is "recoverable" is beside the point if I did not consider the possibility of it being raised in the first place. By the time the code is ready to be called "Production", logging in many cases is all the handing that remains. Aug 30, 2016 at 19:45
5

It means not littering your code with exception handlers. It also means don't do this:

try
{
 //Your code here
}
catch (Exception exception)
{

}

Why is this bad? Catching any exception is not desirable because what if the system is giving you a system exception or out of memory exception? Do you really want your application to continue when the system is telling you there is no more memory?!

So, exception handlers should be specific and should be recoverable. You can always have a high level generic application level exception handler to log those cases when something unexpected occurred rather crashing your application. This is done so your application can shut down gracefully. But other than that, keep exception handlers to a minimum and specific to certain scenarios.

7
  • I see empty catch blocks all the time here. When I ask the programmer about it, the answer is almost invariably: "I don't want the program to crash". sigh... Aug 30, 2016 at 17:40
  • This is also sometimes referred to as 'Pokemon exception handling' (gotta catch em all). Aug 30, 2016 at 19:46
  • 3
    "Do you really want your application to continue when the system is telling you there is no more memory?" -- actually yes, but only because I was raised in systems programming and therefore have unrealistic ideas about robustness. If an application can (a) abort the operation that was using loads of memory; (b) let the gc reclaim the loads of memory it was using; (c) inform the user that the operation couldn't be completed; (d) survive any OOM-killer or similar that the OS has; then it can continue. Aug 30, 2016 at 19:51
  • 4
    ... so for example, if I try to open a file that's too big for my text editor to handle in available memory, actually I don't want my text editor to abort. I want it to cleanly fail to open that file (or you know, if we're wishing, I want it to manage the text file without loading the whole thing into memory at once, but we can't have everything. Some operations use a lot of memory). Aug 30, 2016 at 19:54
  • @Steve - Then you would have catch (OutOfMemoryException outOfMemoryException), nothing wrong with that. Maybe my memory example does not fit all use cases.
    – Jon Raynor
    Aug 30, 2016 at 20:09
2

"Astronauts have a saying that, in space, there is no problem so bad you can't make it worse." - Chris Hatfeld

When we catch exceptions, we have taken a very unusual path through the code. Typically function calls enter at the top of the function body, and end at either the end of the body or at a special return statement. When an exception occurs, neither of those two things happen. We instead follow a completely different path through the code using stack unwinding. Invariants may fall apart in this case.

When we do this, it is very possible to have unintended side effects because we skipped past several layers. Consider this faulty bank logic function:

bool transfer(Account accountFrom, Account accountTo, int amount)
{
    try {
        accountFrom.debit(amount); // remove the specified amount from the account
    } catch (NotEnoughFunds ex) {
        return false; // we failed
    }
    accountTo.credit(amount);
    return true;
}

Now this is pathologically bad as far as banking apps can go, but we do see some attempt to catch the exceptions. However, let's say accountTo.credit(amount) raises a NotImplementedException. What happens now? We unwind the stack, and maybe the layer above prints a helpful log message before continuing. This would be where the real problem occurs. The exception that occurred has invalidated an assumption of the banking application: we have debited one account but not credited another. If we "handle" it, then this assumption is no longer valid from here on out, and the programmer likely never accounted for this. The accountFrom may get audited to explain where this money went, or the entire system may come crashing down as the system tries to balance an equation that is now unbalanceable.

If you want to catch an exception, you need to understand that exception well enough to return the system to a state where no invariants have been broken. In the best of all cases, the code you called does this for you, because the author wrote the code with exceptions in mind. However, it is very common for an author to be exception unaware yet use a library under the hood which is exception aware. In this case, you may catch the underlying library's exception, and maintain its invariants, but fail to correct the invariants of the layer inbetween.

If an invariant is violated, there are very few things we can do. In many environments, it is valid to simply exit the application with an error saying "someone did something I didn't think was possible... we're stopping now." In other environments you are expected to cauterize the wound, isolating how much damage the invariant can do, and excising the damage in a way which doesn't break any more invariants. For example, you might design a system around C#'s AppDomain system, where individual components are separated into different AppDomains and are only allowed to communicate via channels you control. You may be able to design the rules of those channels such that when a thread in one domain throws an unhandled exception, you can tear down that entire AppDomain (because its "invariants" are no longer trusted), and then use your control over the communication channels to manage the side effects of that domain being removed in a way which preserves the invariants of the other domains.

1

Although as the quote says the correct way to handle exceptions is to let them pass up--when you are at the top level, for instance if you are creating your own thread and it has a top-level loop, you nearly always want to catch all exceptions so that your thread doesn't break out and die.

You either want to catch all the exceptions inside the loop so it can continue (at least log them!) or you want to catch them outside the loop, log them and halt the thread. This is what allows you to mostly ignore exceptions in other parts of your program--knowing that it will be handled correctly somewhere up the call chain.

This is true for thread loops, for your main system loop (if you have one) and for any event handlers that don't already have predefined exception handling that suits your needs.

1

When there is an exception that you don't know how to handle, either there is someone else who knows how to handle it or not. And either the exception happens or it doesn't.

If it doesn't happen, catching the exception makes no difference but is a waste of effort. If it happens and nobody knows how to handle it and you catch it, there's no benefit coming from that - the only benefit would come from someone figuring out how to handle it. So again, a waste of effort. And if there is someone who knows how to handle it, catching it doesn't help but might keep someone from handling it properly.

There is no situation where catching an exception that you don't know how to handle would be helpful.

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