Living with unchecked exceptions

Hey there fabulous readers: I’d like to get your opinions on unchecked exceptions.

Before you run off down to the comments and start typing a short novel, I want to make sure that we’re all on the same page here.

To start with: “checked exceptions” is the feature perhaps best known in Java whereby a method M must state which exceptions it may throw. If method N calls method M then either N must handle those exceptions or it must state that it can also throw them, and so on. Basically it makes exceptions an explicit part of the contract of a method, right up there with its formal parameter types, return type, and so on.

// Java
private void someMethod() throws SomeException
{ ... }

This is a somewhat controversial feature of Java. Opinions vary widely as far as whether the feature achieves its design goals effectively or actually creates more problems than it solves. The original designers of C# rejected checked exceptions and haven’t much regretted it; you can read Anders’ thoughts on it here.

My goal here is not to rehash that debate; there is approximately zero chance that this feature is going to be added to C# now, so advocating for or against that position is not a good use of your time. Rather, I want to talk about the fact that C# has had unchecked exceptions this past decade, and somehow people must have adapted to the fact that the set of exceptions a method may throw cannot be determined from the method signature. My questions for you are:

1) Have your programs had bugs that you fixed by adding exception handling for a specific exception that you did not realize could be thrown?

2) When you are writing new code that calls methods you didn’t write, how do you know what exceptions you should be handling? Are there particular exceptions that you believe should always be handled regardless of what method produces them? For example, is it the case that any method that can possibly throw IOException should always be inside a try-catch?

3) How much weight do you give to consistency of exception handling? For example, if you are writing new code to call a method M, and nine times out of ten elsewhere in your program the call sites for M wraps the call with a try catch that catches FooException, do you see that as a good reason to catch the same exception in the new code? Or is that a false generalization?

Please leave any thoughts in the comments; I’ll follow up with a summary of responses in a later episode. Thanks!

83 thoughts on “Living with unchecked exceptions

  1. 1) Yes, but rarely. Most of the time it is for defensive coding or to give the user a better error message rather than fix a bug. I’ve seen code that catches > 6 exceptions and handles each the exact same way. Then a 7th was thrown, so the developer added yet another handler that did the same thing. Ugh.

    2) We ask developers to use XML Documentation on internal methods that directly throw exceptions. However, methods that call other methods that could throw are easily buried. There is no way to really determine what could legitimately throw. As a quick example, lets say we have validated all inputs within a class then call some framework method that could throw an exception due to a null parameter that by our internal contract should never ever be null. You wouldn’t want to catch this at all because the fact that it is thrown indicates a bug in your contract.

    One paradigm that I struggle with enforcing is consistent use of exception declarations for Interfaces. A clear example of this is methods on an interface that operates a class of physical hardware that has (or may have) multiple implementations (lets say this hardware may be Serial/TCP/Bluetooth or even Interop/COM) where errors need to be thrown, but the caller should be somewhat shielded from the details of the implementation that could fail. In this case, the caller should only care about whether an exception is transient-could-retry, transient-requires-user-interaction-to-correct, or fatal. Based on implementation, any one framework exception could be in multiple categories thus implementations of the interface should only throw the exceptions defined in the XML doc of the interface.

    3) This should be a false generalization. Looking at lots of code written by others, it seems to be a generalization, and used in places where it shouldn’t be. If you are asking in the context of making a ‘rule’ for analysis, I’d be surprised if seeing this happen in isolation has much weight. If you attempted to build an analysis rule around this from code I just saw yesterday, you’d warn about the need to catch an exception on ALL calls to int.parse().

  2. This is going to be a controversial opinion but I’ll write it anyway… I believe that today when so many .NET APIs actually have generic catch blocks built within them*, there’s no longer a need to fear the “catch (Exception)” block – with proper handling for critical exceptions of course (out of memory, etc.) Moreover, when you catch a specific exception that’s been documented, you don’t know that it actually originated from where you excepted it to!

    For me, these catch blocks usually appear at the top of the call stack, and are mostly used for logging purposes. There are only a few cases where catching something specific has real value.

    * Tasks, WCF Services and WPF Bindings to name a few. All of these actually swallow any exception, and won’t crash the process.

  3. 1) Almost never. However, I frquently deal with code where other people didn’t realize a specific exception could be thrown. Usually, this has to do with the handling of null values, where checked exceptions wouldn’t help.
    I also deal with a lot of code that catches exceptions which should not be caught, and that’s probably quite worse.

    2) Usually, when calling a method I didn’t write, I more or less understand the kind of things the method is going to do. Thus, I have a somewhat good idea about the exceptions that could be thrown (and which would matter to me). In this case, I read the documentation to discover which exceptions I should catch for the cases which matter to me.
    Most of the time, I won’t catch exceptions until I have a good reason to catch them. (Like knowing that a file copy can fail, when I want my specific code to ignore failed copy operations… or possibly retry them.)

    Here, I really don’t think there are exceptions that should always be caught. Every program is different, and there are things which may matter in one scenario and not in one another.
    e.g. While it’s true that every file I/O operation might fail, I don’t think every program doing file I/O needs to handle by itself the failure of a file I/O operation.

    Would a console application simply writing data to a file whose name is supplied by the user need to handle the cases where the file operation fails ? Or more explicitly, for what reason would I want to catch this exception ?
    In this simple case, I’d catch this exception if i wanted to print a more user-friendly message to the user. Or I’d do it if I wanted to control the exit code of the process in case of failure.
    Do I need to do this for my application ? Is it critical that my user doesn’t see an ugly IOException dumped to the console ? Does the exit code really have to have a specific value for identifying this case ?
    Maybe, or maybe not. It really depends on the specific application.

    3) IHMO, methods absolutely requiring to handle specific exceptions (in a specific way ?) likely have a flaw in their design.
    If I want to use another method that is wrapped everywhere else in a try/catch block, I’ll first analyze the reason fo that repeated catching.
    If the catching is really needed, I’ll likely try to encapsulate the exception catching for this specific method call in another method, for later reuse.
    Anyway, I think understanding what the code does (or tries to do) is the key here.

  4. 1) Have your programs had bugs that you fixed by adding exception handling for a specific exception that you did not realize could be thrown?

    Yes. Unchecked exceptions do prevent some level of discoverability in our APIs, and this is a natural consequence. That being said, most of the time, exceptions should by nature halt the execution of the request/workflow – so this happens rarely. Most of the time that I encounter an exception that I didn’t realize could be thrown, it simply makes me aware of a bug in my usage of an API and I fix it, not by adding a catch, but by fixing my usage.

    2) When you are writing new code that calls methods you didn’t write, how do you know what exceptions you should be handling?

    Decompilation and documentation are really the only two answers that I can reasonably give here. Then again, the likelihood that I should be catching an exception anywhere but the UI/request handler level is low and therefore I tend to not make a large investment in researching potential exceptions. This is only not true in the cases where the developer of the API used exceptions inappropriately.

    2.5) Are there particular exceptions that you believe should always be handled regardless of what method produces them? For example, is it the case that any method that can possibly throw IOException should always be inside a try-catch?

    No. Because of the exception class hierarchy, I cannot make a statement that there are *always* cases where exceptions should always be handled. It depends on circumstance. I think that unless you have a very good sense of your application, you run the risk of producing more bugs by trying to recover early from exceptions. Recovering at the UI/request handler level, however, is sometimes feasible based off of the exception – but this is a rare case. Most exceptions should be caused by bugs or unrecoverable system states. IOExceptions really should only be treated differently in that they should either try the workflow again, or format the error differently to the user (for example, please select a new file to save to or to open).

    3) How much weight do you give to consistency of exception handling? For example, if you are writing new code to call a method M, and nine times out of ten elsewhere in your program the call sites for M wraps the call with a try catch that catches FooException, do you see that as a good reason to catch the same exception in the new code? Or is that a false generalization?

    That is false – especially since it depends on the level that you are working on. If those 9 times, the exception was caught at a UI level, then that makes no statement of how to appropriately use that method at a lower level.

    Of course, when using a method you’ve never used before, you should do a quick usage find of that method to get a better feeling for how that method should be used. This can cause you to add a catch block when you wouldn’t have before, but that isn’t going to be always the case.

    ————-

    Unchecked exceptions are very nice, in my opinion. They make the mechanism useful without enforcing a bunch of ceremony on developers. As long as people use them properly, then all will be good.

    I will point out one last thing: certainly catching and rethrowing can be an appropriate strategy for certain things. For example, I may have a catch-all handler between my payment services and request handler layer so that errors produced by that module of code get logged differently (for example, recording all API calls to third party systems). I’m not referring to this kind of scenarios in the rest of my comment however, because this isn’t an example of early recovery.

    • I agree that early recovery is rare and not a good driver for requiring people to use restrictive language features. I just find the kind of exception pattern I’m talking about invaluable for (i) providing better feedback, both the user and whoever does support (me, often enough), and (ii) sometimes, late catches fail a whole operation when some parts of the operation could have been successful and would have been useful to the user. Say, populating as many fields of a form as you can, and erroring out the one field that broke.

  5. 1) Yes, although it is not often.
    2) I had a plugin for resharper which would let me know, I believe though XmlDoc. It is better to isolate IO with safety layers, so yes. I would prefer languages move toward Haskell, or even better Koka with regard to this – separate effectful code from pure or total code.
    3) False generalization I think. Why is it wrapped in 9 out of 10 places? I would need that answer – sounds like we are already covering up for other problems.

  6. 1) On a few rare occasions, yes.
    One of them was actually related to a BCL method. I checked the documentation and read that if the input was incorrect, null would be returned. Turns out that very specific wrong input actually thew a FormatException or something, which was documented, but TL;DR happened.

    2) Usually yes. Also like Marc says, it usually doesn’t matter which exception occurs, the handling is pretty much the same.

    3) I’m not sure if it is a good generalization. It sounds like a good idea, but I can imagine there are cases where the exception is handled further up the call stack. For example, there are two overloads of some method, and one of the overloads (A) is called by the other (B). In order to get the same exception behavior for these methods, B would not have to catch any exceptions. However, if I were using overload A directly elsewhere in my software, I should handle those exceptions.
    I doubt that this is the only exceptional case (pun intended), but it leads me to believe that it is in fact call site dependant.

  7. 1) No

    2) Reading documentation for the method is good enough approach to know what exceptions to expect. And each call site’s exception handling approach can be different, so there should be no requirement to handle something.

    3) Each call site should be as concise and readable as possible, without sacrificing error handling. There is no need for all call sites to use same exception handling pattern.

  8. Though several have suggested that exceptions which cannot be handled and resolved should only be caught at the outer boundary of the application, I much prefer the idea of catching and rethrowing exceptions. First, if the exception is only caught at the top, pretty much all I will have to use for troubleshooting is going to be a call-stack and though that is sometimes enough, a lot of the time that generates more questions than answers. On the other hand, if I catch an exception, I might not know what it is or how to handle it, but I do know what my code was trying to do when it occurred and I can log useful information about that to aid in troubleshooting before rethrowing the exception. Likewise in some cases there is an advantage to wrapping the exception with a new one as I can use that to communicate information to the level above (which is also under my control) which may improve its ability to make a graceful exit or help guide it in what information might be the most useful to log.

    Finally, I like the idea of writing exception handlers to deal with such things. The primary function would be logging information and then rethrowing the exception, with the ability to customize in a given context what that information should be, but it has the added advantage that, should one discover that a specific type of exception may be thrown which can always, or in certain contexts, be handled, one can modify the exception handler to do something other than log and throw when that exception is caught without having to modify every catch clause where it might be caught.

  9. 1. Yes. It’s the FileNotFoundException when it then turns out that it can also be DirectoryNotFoundException (the *file* doesn’t exist, but neither does its parent directory). Having said that, I don’t think checked exceptions are a good way of fixing this. This is just an unfortunate API design choice.

    2/3: no opinion.

  10. 1) Have your programs had bugs that you fixed by adding exception handling for a specific exception that you did not realize could be thrown?

    Not that I recall

    2) When you are writing new code that calls methods you didn’t write, how do you know what exceptions you should be handling? Are there particular exceptions that you believe should always be handled regardless of what method produces them? For example, is it the case that any method that can possibly throw IOException should always be inside a try-catch?

    I think that exceptions are not designed to be handled
    locally. It’s been taught to me (by trustworthy people, I
    hope) that exceptions are expensive to create. It is
    therefore poor design to rely on throwing exceptions
    as an alernative method of returning a result from a
    method. In my experience, the majority of exceptions
    are raised because some precondition of the method
    is not being met. One can avoid them by ensuring
    that the precondition is met. If this cannot be ensured,
    it is often best practice to throw your own exception,
    describing the precondition in the domain specific
    terms of your current method, which is at the level of
    abstraction that the caller is thinking in terms of. For
    the rest, a global exception handler is almost always
    the best solution in my experience.

    3) How much weight do you give to consistency of exception handling? For example, if you are writing new code to call a method M, and nine times out of ten elsewhere in your program the call sites for M wraps the call with a try catch that catches FooException, do you see that as a good reason to catch the same exception in the new code? Or is that a false generalization?

    I’ve never run into this case, unless you count using
    statements, which do not actually catch the
    exception. In general, I would say that if a method is
    written so that you should or have to handle a certain
    exception, then it is a result of poor api design. It
    would seem that the method was actually designed
    two have two return types. The information provided
    by such an exception should instead be reflection
    within the structure of the return type.

  11. Context: A C# software with more than 6 million LOC (about half of it generated by various generators), with more than 40 people having contributed over the last 8 years. A large part of the software works against a database, but also external and internal web services are consumed. The software is a mix of Silverlight and classic .Net. I Interpret your “you” in the following as the complete team as far as I (one of the primary architects and programmers) can oversee the code (no only me, the person). And I risk to tell the truth here 😉 (as I see it).

    Important consideration: There are completely different sorts of “try/catch”es: (a) Rethrows; we use these frequently and without much ado for logging local context information to Log4Net. It is customary to “catch” everything (Exception) there – but of course, this is not real exception *handling*, but does a naked throw at the end. (b) “Converting exception to other error signal” – e.g. message box at GUI, return code in main method, some exception propagation to a parent thread; this is really the same as (a), only “disguised” as a sort “catching” for technical reasons. Customarily, we catch (Exception ex) here also. (c) “Giving up” (and e.g. retrying): In many cases, anything that goes wrong leads to the conclusion that either the “request” (“the job to be done”) cannot be done or has to be retried from the beginning (even if it is split into many transactions – we try to make such algorithm idempotent): Also here, we catch everything and “handle” it by logging and giving up; or logging and starting some retry algorthm. (d) Very few cases where we define our own exception classes: This are almost always tricky, consciously designed “control flow by exception” algorithms, where the specific exception type must not bubble out from the algorithm – so in this case, there is obviously a catch block for this special design. (e) “Real exception handling”: The remaining few percent (I have not counted them) of real exception handling usually have “grown evolutionary” and therefore mostly inconsistently: Failures that have occurred have lead to changes in the try-catch blocks – usually more specific catches before an already existing (because of (a), (b), (c)) generic catch (Exception) block.

    1) Have your programs had bugs that you fixed by adding exception handling for a specific exception that you did not realize could be thrown?

    Yes – see case (e) above.

    2) When you are writing new code that calls methods you didn’t write, how do you know what exceptions you should be handling? Are there particular exceptions that you believe should always be handled regardless of what method produces them? For example, is it the case that any method that can possibly throw IOException should always be inside a try-catch?

    As described above, it seems most in our shop start with “safety-net catch-all” blocks.

    3) How much weight do you give to consistency of exception handling? For example, if you are writing new code to call a method M, and nine times out of ten elsewhere in your program the call sites for M wraps the call with a try catch that catches FooException, do you see that as a good reason to catch the same exception in the new code? Or is that a false generalization?

    This is, as a design rule, not true for our code. In practice, the pattern still occurs quite often, because of shared failures or copy-paste-coding or knowledge about called methods.

    Regards
    H.M.

  12. I’d echo Marc Gravell’s statements.

    The type of exception usually doesn’t matter – usually just that one happened, so we clean up our local mess and pass it along; occasionally rewrapping it if interface documentation requires it or you want to indicate that some class of operation failed (no matter what the cause).

    The most important diagnostic is almost always the stack trace, so the ability to rewrap an exception is pretty important, or to properly re-throw one without overwriting the stack trace.

    I used to despair at the sheer number of exception handlers present in some Java programs because of checked exceptions – and the difficulty of keeping the error handling consistent over time as a result.

Leave a comment