Why Is the Catch(Exception) Almost Always a Bad Idea

Why is the Catch(Exception) almost always a bad Idea?

Because when you catch exception you're supposed to handle it properly. And you cannot expect to handle all kind of exceptions in your code. Also when you catch all exceptions, you may get an exception that cannot deal with and prevent code that is upper in the stack to handle it properly.

The general principal is to catch the most specific type you can.

Why are empty catch blocks a bad idea?

Usually empty try-catch is a bad idea because you are silently swallowing an error condition and then continuing execution. Occasionally this may be the right thing to do, but often it's a sign that a developer saw an exception, didn't know what to do about it, and so used an empty catch to silence the problem.

It's the programming equivalent of putting black tape over an engine warning light.

I believe that how you deal with exceptions depends on what layer of the software you are working in: Exceptions in the Rainforest.

Is it really that bad to catch a general exception?

Obviously this is one of those questions where the only real answer is "it depends."

The main thing it depends on is where your are catching the exception. In general libraries should be more conservative with catching exceptions whereas at the top level of your program (e.g. in your main method or in the top of the action method in a controller, etc) you can be more liberal with what you catch.

The reason for this is that e.g. you don't want to catch all exceptions in a library because you may mask problems that have nothing to do with your library, like "OutOfMemoryException" which you really would prefer bubbles up so that the user can be notified, etc. On the other hand, if you are talking about catching exceptions inside your main() method which catches the exception, displays it and then exits... well, it's probably safe to catch just about any exception here.

The most important rule about catching all exceptions is that you should never just swallow all exceptions silently... e.g. something like this in Java:

try { 
something();
} catch (Exception ex) {}

or this in Python:

try:
something()
except:
pass

Because these can be some of the hardest issues to track down.

A good rule of thumb is that you should only catch exceptions that you can properly deal with yourself. If you cannot handle the exception completely then you should let it bubble up to someone who can.

Give (an) example(s) of something bad that happens with a 'catch(Exception)' clause

Catching a NullPointerException can result in the application stumbling onward oblivious to the fact that something it needed was not done. Later the application will fail again or behave abnormally because that previous operation did not complete, but it will be harder to figure out now.

Eating an exception within a transaction can result in the transaction not getting rolled back when it should have been. You could end up with inconsistent data.

Eating interruptedException:

public void run() {
while (!Thread.currentThread().isInterrupted()) {
try {
doSomeWork();
Thread.sleep();
} catch (Exception e) {
}
}
}

If the thread gets interrupted while sleeping, the interrupted flag will get reset when the sleep method throws InterruptedException, since the catch doesn't restore the interrupt flag the while loop condition stays false and the thread doesn't exit.

Similarly, since InterruptedIOException is a subclass of IOException: if you have network IO code that only handles IOException then you can fail to restore the interrupt flag on and fail to detect that your thread has been interrupted.

Why do we follow narrow to broad Exception catching mecahnism in java?

Because in case of a specific exception that you are actually expecting to happen, you are going to handle it in a different way. For example, you may want to create the file when you get FileNotFoundException.

If you go broad to narrow as in the example you provided, then FileNotFoundException will never be caught, because a broader one, Exception is used first. So broad to narrow is useless.

Why is except: pass a bad programming practice?

As you correctly guessed, there are two sides to it: Catching any error by specifying no exception type after except, and simply passing it without taking any action.

My explanation is “a bit” longer—so tl;dr it breaks down to this:

  1. Don’t catch any error. Always specify which exceptions you are prepared to recover from and only catch those.
  2. Try to avoid passing in except blocks. Unless explicitly desired, this is usually not a good sign.

But let’s go into detail:

Don’t catch any error

When using a try block, you usually do this because you know that there is a chance of an exception being thrown. As such, you also already have an approximate idea of what can break and what exception can be thrown. In such cases, you catch an exception because you can positively recover from it. That means that you are prepared for the exception and have some alternative plan which you will follow in case of that exception.

For example, when you ask for the user to input a number, you can convert the input using int() which might raise a ValueError. You can easily recover that by simply asking the user to try it again, so catching the ValueError and prompting the user again would be an appropriate plan. A different example would be if you want to read some configuration from a file, and that file happens to not exist. Because it is a configuration file, you might have some default configuration as a fallback, so the file is not exactly necessary. So catching a FileNotFoundError and simply applying the default configuration would be a good plan here. Now in both these cases, we have a very specific exception we expect and have an equally specific plan to recover from it. As such, in each case, we explicitly only except that certain exception.

However, if we were to catch everything, then—in addition to those exceptions we are prepared to recover from—there is also a chance that we get exceptions that we didn’t expect, and which we indeed cannot recover from; or shouldn’t recover from.

Let’s take the configuration file example from above. In case of a missing file, we just applied our default configuration and might decide at a later point to automatically save the configuration (so next time, the file exists). Now imagine we get a IsADirectoryError, or a PermissionError instead. In such cases, we probably do not want to continue; we could still apply our default configuration, but we later won’t be able to save the file. And it’s likely that the user meant to have a custom configuration too, so using the default values is likely not desired. So we would want to tell the user about it immediately, and probably abort the program execution too. But that’s not something we want to do somewhere deep within some small code part; this is something of application-level importance, so it should be handled at the top—so let the exception bubble up.

Another simple example is also mentioned in the Python 2 idioms document. Here, a simple typo exists in the code which causes it to break. Because we are catching every exception, we also catch NameErrors and SyntaxErrors. Both are mistakes that happen to us all while programming and both are mistakes we absolutely don’t want to include when shipping the code. But because we also caught those, we won’t even know that they occurred there and lose any help to debug it correctly.

But there are also more dangerous exceptions which we are unlikely prepared for. For example, SystemError is usually something that happens rarely and which we cannot really plan for; it means there is something more complicated going on, something that likely prevents us from continuing the current task.

In any case, it’s very unlikely that you are prepared for everything in a small-scale part of the code, so that’s really where you should only catch those exceptions you are prepared for. Some people suggest to at least catch Exception as it won’t include things like SystemExit and KeyboardInterrupt which by design are to terminate your application, but I would argue that this is still far too unspecific. There is only one place where I personally accept catching Exception or just any exception, and that is in a single global application-level exception handler which has the single purpose to log any exception we were not prepared for. That way, we can still retain as much information about unexpected exceptions, which we then can use to extend our code to handle those explicitly (if we can recover from them) or—in case of a bug—to create test cases to make sure it won’t happen again. But of course, that only works if we only ever caught those exceptions we were already expecting, so the ones we didn’t expect will naturally bubble up.

Try to avoid passing in except blocks

When explicitly catching a small selection of specific exceptions, there are many situations in which we will be fine by simply doing nothing. In such cases, just having except SomeSpecificException: pass is just fine. Most of the time though, this is not the case as we likely need some code related to the recovery process (as mentioned above). This can be for example something that retries the action again, or to set up a default value instead.

If that’s not the case though, for example, because our code is already structured to repeat until it succeeds, then just passing is good enough. Taking our example from above, we might want to ask the user to enter a number. Because we know that users like to not do what we ask them for, we might just put it into a loop in the first place, so it could look like this:

def askForNumber ():
while True:
try:
return int(input('Please enter a number: '))
except ValueError:
pass

Because we keep trying until no exception is thrown, we don’t need to do anything special in the except block, so this is fine. But of course, one might argue that we at least want to show the user some error message to tell him why he has to repeat the input.

In many other cases though, just passing in an except is a sign that we weren’t really prepared for the exception we are catching. Unless those exceptions are simple (like ValueError or TypeError), and the reason why we can pass is obvious, try to avoid just passing. If there’s really nothing to do (and you are absolutely sure about it), then consider adding a comment why that’s the case; otherwise, expand the except block to actually include some recovery code.

except: pass

The worst offender though is the combination of both. This means that we are willingly catching any error although we are absolutely not prepared for it and we also don’t do anything about it. You at least want to log the error and also likely reraise it to still terminate the application (it’s unlikely you can continue like normal after a MemoryError). Just passing though will not only keep the application somewhat alive (depending on where you catch of course), but also throw away all the information, making it impossible to discover the error—which is especially true if you are not the one discovering it.


So the bottom line is: Catch only exceptions you really expect and are prepared to recover from; all others are likely either mistakes you should fix or something you are not prepared for anyway. Passing specific exceptions are fine if you really don’t need to do something about them. In all other cases, it’s just a sign of presumption and being lazy. And you definitely want to fix that.

Is handling all possible (unchecked) Exceptions good practice?

Best practice is: catch all exceptions separately and make as more custom log messages as possible. It's easier to understand faulty situations and to act accordingly.

Reality in a typical business application is: try to group your exceptions (ex. group all possible exceptions caused by your method input, all the ones throwed by the db etc. etc.) and learn that sometimes you will put the infamous catch (Exception e) or rethrow a generic exception with a generic message... or people will start calling you about logs growing like elephants.

Is it a bad practice to catch Throwable?

You need to be as specific as possible. Otherwise unforeseen bugs might creep away this way.

Besides, Throwable covers Error as well and that's usually no point of return. You don't want to catch/handle that, you want your program to die immediately so that you can fix it properly.

Why catch Exceptions in Java, when you can catch Throwables?

It all depends a bit on what you're going to do with an Error once you've caught it. In general, catching Errors probably shouldn't be seen as part of your "normal" exception flow. If you do catch one, you shouldn't be thinking about "carrying on as though nothing has happened", because the JVM (and various libraries) will use Errors as a way of signalling that "something really serious has happened and we need to shut down as soon as possible". In general, it's best to listen to them when they're telling you the end is nigh.

Another issue is that the recoverability or not from an Error may depend on the particular virtual machine, which is something you may or not have control over.

That said, there are a few corner cases where it is safe and/or desirable to catch Errors, or at least certain subclasses:

  • There are cases where you really do want to stop the normal course of flow: e.g. if you're in a Servlet, you might not want the Servlet runner's default exception handler to announce to the world that you've had an OutOfMemoryError, whether or not you can recover from it.
  • Occasionally, an Error will be thrown in cases where the JVM can cleanly recover from the cause of the error. For example, if an OutOfMemoryError occurs while attempting to allocate an array, in Hotspot at least, it seems you can safely recover from this. (There are of course other cases where an OutOfMemoryError could be thrown where it isn't safe to try and plough on.)

So the bottom line is: if you do catch Throwable/Error rather than Exception, it should be a well-defined case where you know you're "doing something special".

Edit: Possibly this is obvious, but I forgot to say that in practice, the JVM might not actually invoke your catch clause on an Error. I've definitely seen Hotspot glibly gloss over attempts to catch certain OutOfMemoryErrors and NoClassDefFoundError.

Is it bad practice to catch RuntimeException for logging purposes?

It's not always bad to catch RuntimeException's. But even if your team decided to not catch RuntimeException's, you can always catch it, log something and then re-throw it. It won't change your application logic at all.

Almost all logger libraries nowadays have possibility to log different details about the exception (like a stacktrace and all nested exceptions as well) in addition to log message.

public void doStuff(String param){
try {
process(param);
} catch(RuntimeException e) {
logger.error("Something weird happened while processing " + param, e);
throw e;
}
}


Below is an update, about the context, thank you Ralf Kleberhoff for pointing on that

It's better to log a message about RuntimeException (or any other Exception) only on a top levels of your application, to avoid duplicate messages about the same exception in the log.

If you just want to add some context (like a parameter value, as Ralf Kleberhoff mentioned) and it's not the application top level catch (and you are sure, that top level catch actually exists ), it's better to create a new Exception and add original Exception as a cause into the new one.

public void doStuff(String param){
try {
process(param);
} catch(RuntimeException e) {
throw new RuntimeException("Something weird happened while processing " + param, e);
}
}

private void process(String value){
throw new IllegalStateException("Not implemented yet!");
}


Related Topics



Leave a reply



Submit