How to Design Exception Hierarchies


I still get a lot of questions on how to design exception hierarchies, despite several attempts to describe it in talks, the FDG book, and in posts on this blog. Maybe the guidance gets lots in the in the complexities of the full guidance surrounding exception handling or I am a bad communicator. Let me assume the former :-), and so here is one more attempt at describing the guidance in the most succinct way I am capable of:


·         For each error condition you reusable routine can get into, decide whether the condition is a usage error or a system error.


o   A usage error is something that can be avoided by changing the code that calls your routine. For example, if a routine gets into an error state when a null is passed as one of its arguments (error condition usually represented by an ArgumentNullException), the calling code can modified by the developer to ensure that null is never passed. In other words the developer can ensure that usage errors never occur.


o   A system error is something that cannot be avoided by simply writing code that tries to avoid the error condition.  For example, File.Open gets into an error condition when it tries to open a file that does not exist (and it throws FileNotFoundException). This error condition cannot be fully avoided. Even if you check whether the file exists before calling File.Open, the file can get deleted or corrupted between the call to File.Exists and the call to File.Open.


·         Usage errors need to be communicated to the human developer calling the routine. The exception type is not the best way to communicate errors to humans. The message string is a much better way. Therefore, for exceptions that are thrown as a result of usage errors, I would concentrate on designing a really good (that is explanatory) exception message and using one of the existing .NET Framework exception types: ArgumentNullException, ArgumentException, InvalidOperationException, NotSupportedException, etc. In other words, don’t create new exception types for usage errors because the type of the exception does not matter for usage errors. The .NET Framework already has enough types (actually too many, IMHO) to represent every usage error I can think of.


·         System errors need to be further divided into two groups:


o   Logical errors are system errors that can be handled programmatically. For example, if File.Open throws FileNotFoundException, the calling code can catch the exception and handle it by creating a new file. (Side note: this is in contrast to the usage error described above where you would never first pass a null argument, catch the NullArgumentException, and this time pass a non-null argument).


o   System failures are system errors that cannot be handled programmatically. For example, you really cannot handle out of memory exception resulting from the JIT running out of memory.


·         System failures should result in the shutdown of the process. This might sound scary at the first read, but I would say it’s by-definition: a system failure is an error that can neither be handled by the developer or the program. The best way to shut down a process in such cases is to call Environment.FailFast, which logs the state of the system, and that is very useful in diagnosing the problem. The good thing is that system failures are extremely rare in reusable libraries. They are mostly caused by the execution engine.


·         Logical errors are errors that can be, and often are, handled in code. The way to handle such errors is to catch the exception and execute some “compensating” logic. Whether the catch statement executes is determined by the type of the exception the catch block claims it can handle. This means that logical errors are are condition (and actually the only conditions) where the exception type matters and when you should consider creating a new exception type.


·         If you think you are dealing with a logical error, I would validate this belief by actually writing code or describing very precisely what would the catch handler do when it catches the exception to allow the program to continue its execution. If you cannot describe it or the error can be avoided by changing the calling code, you are dealing with a usage error or a system failure.


·         Note that logical errors are still pretty rare. As a rule of thumb, I would say that error conditions in a typical reusable library fall into: <1% of system failures, 5% logical errors, and the rest ~95% are usage errors.


·         If you are convinced that your error is a logical error, you need to decide whether to reuse one of the existing exception types from the framework or create a new exception type.


o   You should use an existing Framework exception is both of the following are true


§  The exception type makes sense for your error condition. For example, you don’t want to throw FileNotFoundException if your threadpool implementation runs out of the thread quota.


§  The exception will not make an error condition ambiguous. That is, the code that wants to handle the specific error will always be able to tell whether the exception was thrown because of the error or because of some other error that happens to use the same exception. For example, you don’t want to throw (reuse) FileNotFoundException from a routine that calls Framework’s file I/O APIs that can throw the same exception, unless it does not matter to the calling code whether your code or the Framework threw the exception (BTW, it’s quite often that it does not matter).


o   If you decided not to reuse an exception type from the .NET Framework, you will have to create a new exception type. When you create a new exception type, as a rule of thumb, I would simply inherit it from System.Exception or from a single subtype of System.Exception representing custom exceptions declared in your component/framework. I would create the root only if you have more than 3 custom exceptions. There are limited cases where it’s good to create a more elaborate hierarchy, but it’s extremely rare. I would say in a typical library 99% of custom exceptions should follow this guidance. The reason is that when you catch exceptions you almost never care about the hierarchy. You almost never care about the hierarchy because you almost never want to catch more than one error at a time. You almost never want to handle more than one error at a time, because if the errors could be handled the same way, they should not be expressed using two different exception types. Now, please note that I said “almost” and “should” in several places. You your case falls into the small number of corner cases, you need to create a deeper hierarchy.


o   Lastly, keep in mind that it’s not a breaking change to change an exception your code throws to a subtype of the exception. For example, if the first version of your library throws FooException, in any future version of your library, you can start throwing a subtype for FooException without breaking code that was compiled against the previous version of your library. This means, when in doubt, I would consider not creating new exception types till you are sure you need them. At which point, you can create a new exception type by inheriting from the currently throw type. Sometimes it might result in slightly strange exception hierarchy (for example, a custom exception inheriting from InvalidOperationException), but it’s not a big deal in comparison to the cost having unnecessary exception types in your library, which makes the library more complex, adds development cost, increases working set, etc.

Comments (14)

  1. Tom Kirby-Green says:

    Great post Krzysztof, the whole subject of good design of exceptions keeps coming up at my shop. You latest post helps no end, many thanks! 🙂

  2. Krzysztof Cwalina, owner of the Framework Design Guidelines , has written a great post on How to Design

  3. Thanuja says:

    Thanks KC for the post.

    I would like to know ur idea on following design. Think we have several framework components that fall in to several assemblies such as Caching, Data Access Framework, Configuration, etc. Then we create separate exception classes derived from System.Exception and place in each of the component, such as CachingException, DAException, etc. This type is used to wrap whatever the framework exceptions thrown from the framework APIs and throw/re-throw when necessary.  Since these components can be called in a hierarchical manner (DA calls Configuration, etc, ), I believe it would be easy to track from which component the exception is raised.

    Well would this design make sense?

  4. Hi Krzysztof, grest post. We are an ISV and while we had no problems designing exceptions according to your guidelines, we are having a bit of a head scratch trying to document them in the public API. This is the question posted in my blog http://www.aspose.com/Community/blogs/aspose.words/archive/2007/01/27/66265.aspx. Thanks.

  5. Aspose.Words says:

    According to many customers’ requests we have improved design and documentation related to exceptions…

  6. Alfred Myers says:

    Thanuja,

    You can track the component that raised the exception from the stack trace every Exception provides. Is there any other reason for creating your custom exceptions?

  7. O Krzysztof Cwalina (pronuncia-se cristof sualina) escreveu um post interessante sobre o projeto e uso…

  8. Krzysztof Cwalina says:

    Thanuja, I would not catch and wrap unless you have a good reason for doing it. Being able to track which component raised an exception is not a good reason IMHO; you can get this information from the callstack. A good reason is if you want to abstract the internal implementation. More on this below.

    Roman, documenting exceptions is hard. The reason is that it’s actually not about just documenting exceptions, but more about being very explicit about the contract of the APIs. Specifying, describing, and keeping in compliance with contract requires deliberate design and takes time. But great reusable components must do it. The .NET framework does an ok job here and we are constantly thinking how to improve.

    As to the specific questions, I would definitely document all exceptions you end up throwing from your component, even the ones thrown from the lower layers (e.g. the Framework).

    But, I would be careful with letting implementation details leak through higher level abstractions. For example, you don’t want to throw FileNotFoundException from database access APIs when the database is stored in a file. The fact that the data is stored in a file is an implementation detail that can change in the future. Same for things like configuration system APIs. The .NET config system is file based, but we don’t (or at least I hope we don’t) throw FileIO exceptions when the files get corrupted.

    Now, when you catch and abstract (throw a different exception), you need to ensure that you leave the system in a known state such that client code can now handle your abstracted exception. Is no good to catch, wrap/abstract, and retrow a different exception that cannot be handled anyway because it would leave the internal implementation in an undefined state.

  9. 為什麼推薦 ? Krzysztof Cwalina 是 .NET Framework API Design Team 的 Program Manager, 他也寫了 一本 Framework Design

  10. vijay says:

    Hi Krzysztof, I am asking this question in a different context? how do you define hierarchies in business applicaitons? for e.g. if we have Customer related exceptions then should be have a base class like CustomerException and then let specific customer related exception inherit from this CustomerException like InvalidCustomerException? or there should be more generic BusinessException and for any conditions we throw this business exception. in consideration of Logging, I would like to have some specific business data to be logged. so considering this what are your suggestions for the hierarchy?

  11. Krzysztof Cwalina says:

    Vijay, I think same guidelines apply to bussiness applications: create a new exception if you can articulate reasons for the existance of the exception beyond just wanting to create a hierarchy.

  12. Eran Nachum says:

    Great post, very enjoyed reading it and got a nice prespective about all this issue.

    I am inviting to take a look in my weblog at http://www.eranachum.com

  13. Hi Krzysztof,

    I am currently designing the exception hierarchy for our new product. I started out with the difference between recoverable exceptions and non-recoverable exceptions and ended up at pretty much the same pattern as you did.

    I would like to ask you about the SystemException class in the .NET framework. Is documented as "SystemException is thrown by the common language runtime when errors occur that are nonfatal and recoverable by user programs." Which would mean that it corresponds to what you are calling logical errors.

    However, derived exceptions include ArrayTypeMismatchException, which is IMO a usage error and OutOfMemoryException, which is a system failure.

    So I conclude that the documentation is not really helpful here and that SystemException is not a good base class for custom logical errors.

    You said this implicitly already by advising to use Exception as the base class for such errors.

    Do you agree?

  14. You’ve seen the advice before— it’s not a good programming practice to catch System.Exception . Because