Custom exception types

Today’s topic is about custom exception types. That is whether or not you should write your own exception types to throw in different situations.

Custom exception types: some old guidelines from Microsoft

If you read old guidelines from Microsoft regarding creating custom exceptions, you might remember two base exception types: SystemException and ApplicationException. The idea behind them was that SystemException ought to be used for fatal exceptions: exceptions you can’t recover from. ApplicationException would be used for less serious ones: those that can be caught and processed safely.

NET Framework itself would then introduce descendants of SystemException so that all exceptions thrown by CLR have the single parent. On the other hand, application developers would create their own custom exceptions using ApplicationException base class.

So, in theory, you would end up writing code similar to the following:

try
{
    /* Do something */
}
catch (ApplicationException ex)
{
    /* This exception is not a big deal, move on */
}
catch (SystemException ex)
{
    /* This one is fatal, log and re-throw */
    throw;
}

Here, you execute some code, and if an exception takes place, you look at its type. Application exceptions are recoverable and should not interrupt the program flow. System exceptions are fatal, they signalize an error you are not supposed to suppress.

At first glance, this separation seems logical. All exceptions generated by the Runtime go to the System category; all exceptions declared by application developers - to the Application one.

There’s a problem, however. Whether or not an exception is fatal highly depends on the context it is used in. You can’t just proclaim that all runtime exceptions are fatal in any possible situation. And you can’t state that all exceptions created by non-Microsoft programmers don’t corrupt the execution flow and thus can be safely ignored.

For example, FileNotFoundException inherits from SystemException but it doesn’t mean you need to shut down your application every time it occurs. It may very well be that lack of some file is an expected situation which you can handle accordingly. The same is true for the other way around. It is possible to introduce an application exception which would mean some unrecoverable situation in your code base.

That’s why this initial guideline didn’t take off. Not that Microsoft pushed it hard anyway. Even in .NET Framework itself you can find quite a few exceptions inheriting from ApplicationException instead of SystemException. And of course, nowadays, everyone just uses plain old Exception when they want to introduce a new exception type. I don’t know anyone who follows the said convention.

Custom exception types

Alright, I hope this historical reference was interesting, but that’s not the main topic of this post.

What is the current state of affairs? Today, it is pretty common to see people defining their own exception types, or even hierarchies of custom exception types which represent violations of some business rules. This is how one could look like:

BusinessRuleException
|-- EmailIsNotUniqueException
|-- CustomerException
    |-- CustomerNameIsInvalidException
    |-- CustomerAddressIsInvalidException

BusinessRuleException here is the root exception which serves as a parent for all other custom exceptions.

A slightly different variation of this approach is to just have several custom exceptions not related to each other, all inheriting from the very base Exception.

So, is it a good idea to create custom exceptions? Or should you just use predefined exceptions, such as InvalidOperationException, ArgumentException, and so on? The answer depends on who is going to be the consumer of those exceptions. If the consumer is gonna be yourself, then creating custom exceptions is a terrible idea. Remember, exceptions should be used to state an exceptional situation in your code. Something you don’t expect to happen. In most cases, it means one of two things:

  • You don’t know how to deal with it.

  • This is an error you cannot recover from at runtime, a bug.

When you create a set of custom exceptions for yourself what you essentially do is you are preparing a groundwork for this kind of code:

public string RegisterCustomer(string name, string email)
{
    try
    {
        /* Creating a customer */
    }
    catch (EmailIsNotUniqueException)
    {
        return "The email provided already registered: " + email;
    }
    catch (CustomerNameIsInvalidException)
    {
        return "Customer name is invalid: " + name;
    }
    catch (CustomerAddressIsInvalidException)
    {
        return "Customer address is invalid";
    }
}

While it might look pretty handy and even resemble pattern matching of functional languages, such code is generally a bad idea. It has all the flaws of using exceptions to control the program flow which I described here: it’s unclear if any given statement can throw and whether you should wrap that statement with a try-catch block. To get to know it, you need to examine the internals of the method itself, as well as methods it uses, this information is not present in its signature. Overall, such use of exceptions hinders code readability.

There’s no need to create your own custom exception types if you are going to be the only consumer of them. You shouldn’t ever catch exceptions you throw yourself. Those exceptions signalize unexpected errors in your software, and, according to the Fail Fast principle, you need to let them terminate the current operation completely. And for that, you don’t need a special CustomerNameIsInvalidException, pretty much any exception would fit here. Also, there’s nothing exceptional in an input parameter (customer name) being invalid, that’s just a regular validation error.

The only case where it’s OK to catch exceptions you cast yourself is a try catch block at the very top level of your execution stack. You can log all exceptions there, show a polite apology (or a standard 500 response in case of a web API) and then shut the operation down.

At the very most, define a single custom exception type which you’ll be throwing everywhere to state an invariant violation, something like ContractException.

Custom exception types and external libraries

The situation is a bit different when you are not the only consumer of the exceptions you throw. That is when you write your own redistributable library or framework. You still need to follow the guidelines with regards to catching exceptions you yourself raise. That is you shouldn’t catch them other than for logging purposes. And those exceptions still need to represent errors you don’t know how to deal with.

The difference here is that while you as a library writer don’t know how to handle some unexpected situation in your code, the client that uses your library may know that. For example, if you are building an SMTP client, you don’t know what to do if the SMTP host is unavailable. You can retry sending the email for a couple of times, but what if the host is still not responding? This is a dead end for your library because the further treatment is highly contextual, it depends on a particular situation the library is used in. You can’t possibly know what those situations are, so the only thing you can do here is clearly indicate the crisis by throwing an exception.

At the same time, a client of your library might be fine with not sending some informational email or at least they may know how to handle that failure. This situation is not exceptional for them, they expect it to happen. And so they need to be able to catch the appropriate exception. To do that, the client needs to distinguish that exception from other potential problems because the countermeasures for each of them can be different. This is where custom exception types come into play. They help clients of your library or framework introduce a proper reaction to every possible issue you might encounter.

And just a reminder. If you are a consumer of some library and want to handle some of the exceptions it throws, remember to catch them at the lowest level possible and transform into Result instances. This way, you will be able to avoid all the bad consequences of using exceptions to control the program flow.

Summary

Alright, let’s summarize:

  • Separation into SystemException and ApplicationException is a relict, don’t use any of them. Just use plain old Exception if you need to introduce your own exception type.

  • If you build a software which is not going to be consumed by other programmers, don’t define custom exception types. To state an exceptional situation, throw a predefined one. At most, create a single custom exception for all invariant violations in your code base.

  • If you build a library or framework, do define custom exception types. They will help your clients to properly react to each exceptional situation that you as the library author don’t know how to deal with.

I’ve got a lot of posts dedicated to error handling already. Here are the most important of them:

Subscribe


I don't post everything on my blog. Don't miss smaller tips and updates. Sign up to my mailing list below.

Comments


comments powered by Disqus