Custom exception types

By Vladimir Khorikov

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.

Related articles

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





  • Brian Hinchey

    What about cases where a unit test needs to verify that some functionality throws a specific exception? If a unit test just verifies that any exception is thrown, it might result in false positives where an exception (e.g. null reference) occurs that the unit test was not expecting.

    Note that I do not write a lot of unit tests verifying exceptions, but on the odd occasion I find it important to verify that certain conditions result in a specific exception. In the past I used to use string matching on the exception message to verify that a specific exception has been thrown, but the lack of compilation safety made the tests brittle. So now I create specific exception types so I can verify that exception type was thrown.

    What do you think of this? How do you write unit tests that need to verify that exceptions are thrown in specific conditions?

    • http://enterprisecraftsmanship.com/ Vladimir Khorikov

      Great question.

      In such situations, I personally just create a single custom exception type (ContractException in the article) and expect it in tests. That gives a distinction between system failures (such as null reference exceptions) and business failures, such as invariant violations.

      As far as narrowing the business failure down even further, i.e. getting to know what exact invariant is broken, I don’t think it matters. What matters is that your code reacts properly to some invariant violation: blows up with an exception. If after refactoring it still reacts the same way, and tests confirm that, it means the code still does its job correctly.

      A corollary from this approach is that when I throw an exception myself, I always use the same exception type – ContractException. This way, unit tests can sort out cases handled by the code base (they end up with ContractException) from cases that are not (all other types of exceptions).

  • Harry McIntyre

    I’ve been using my library OneOf (which kinda does f#ish discriminated unions* ) to eliminate the need for exceptions, by using `OneOf`s as return types.

    e.g.

    public OneOf<User, EmailNotUnique, CustomerAddressIsInvalid> RegisterCustomer(string name, string email) {
    ... return any result type instance here... e.g.
    return new EmailNotUnique();
    }
    ...

    var createUserResult = users.CreateUser("");
    return createUserResult.Match(
    u => Redirect("User", u.Id),
    (EmailIsNotUnique x) => View(new { message = x.Message }),
    (CustomerAddressIsInvalid x) => View(new { message = x.Message }),
    );

    This is really powerful as the caller is forced to handle each and every result type (you get a compile error in the .Match if you add a new T to the OneOf).

    * apologies for yet again shilling my libraries all over the internet! Other DU libraries e.g. SuccincT are also available.

    • http://enterprisecraftsmanship.com/ Vladimir Khorikov

      Wow, that looks really cool, I’ll try it out somewhere. I don’t think I ever saw a C# library that handles non-exhaustive matches well.

      • Harry McIntyre

        It’s changed my life 🙂

    • barteloma

      If you save a Product that has 12 parameters and all of them will be check for validation? The code will have parameter explosion

      • Harry McIntyre

        Hmm, I think I picked a bad example. In the case of Validation, having a Type for every case is probably overkill, as a `new ValidationError(fieldName, message)` which is passed to the user covers most things.

        It’s more useful when you the return values need to be handled by different code paths.

        If the handling code is different, then I would suggest that even with 12 cases*, it’s better to use OneOf despite the “parameter explosion” as you have increased compile time safety and expressiveness.

        You can mitigate the long return value by having a custom type that inherits from the OneOfBase.