Choosing the Right Type of Exception to Throw


My last post about the ApplicationException resulted in some questions along the lines of “so, if not ApplicationException, what should I throw instead?” The answer to this question is partially covered by my old post on Exception Throwing and partially by some additional guidelines for creating custom exceptions that did not make it to the original post, but are now included in the FDG book. For your convenience, I included both parts of the guidance below:


Choosing the Right Type of Exception to Throw


After you have decided when you need to throw exceptions, the next step is to pick the right type of exception to throw. This section provides those guidelines.


þ Consider throwing existing exceptions residing in the System namespaces instead of creating custom exception types.


See section 7.3 for detailed usage guidelines of the most common standard exception types.


þ Do create and throw custom exceptions if you have an error condition that can be programmatically handled in a different way than any other existing exception. Otherwise, throw one of the existing exceptions. See section 7.4 for details on creating custom exceptions.


ý Do not create and throw new exceptions just to have ‘your team’s’ exception.


þ Do throw the most specific (the most derived) exception that makes sense.


For example, throw ArgumentNullException and not its base type ArgumentException if a null argument is passed.



Annotation (Jeffrey Richter):


Throwing System.Exception, the base class of all CLS-Compliant exceptions is always the wrong thing to do.


 



Annotation (Brent Rector):


As described in more detail later, catching System.Exception is nearly always the wrong thing to do as well.


Now that you have chosen the correct exception type, you can focus on ensuring that the error message your exception delivers says what you need it to say.


Designing Custom Exceptions


In some cases, it will not be possible to use existing exceptions. In those cases, you’ll need to define custom exceptions. The guidelines in this section provide help on doing that.


ý Avoid deep exception hierarchies. 


þ Do derive exceptions from System.Exception or one of the other common base Exceptions.


þ Do end exception class names with the ‘Exception’ suffix.


þ Do make exceptions serializable. An exception must be serializable to work correctly across application domain and remoting boundaries.


þ Do provide (at least) these common constructors on all exceptions. Make sure the names and types of the parameters are exactly as in the example below.


public class SomeException: Exception, ISerializable {


   public SomeException();


  public SomeException(string message);


  public SomeException(string message, Exception inner);


 


  // this constructor is needed for serialization.


   protected SomeException(SerializationInfo info, StreamingContext context);


}


þ Do report security sensitive information through an override of ToString only after demanding an appropriate permission.


If the permission demand fails, return a string excluding the security sensitive information.



Annotation (Rico Mariani):


Do not store the results of ToString in any generally accessible data structure unless that data structure suitably secures the string from untrusted code.  This advice applies to all strings but since exception strings frequently contain sensitive information (such a file paths) I reiterate the advice here.


þ Do store useful security sensitive information in private exception state. Ensure only trusted code can get the information.


þ Consider providing exception properties for programmatic access to extra information (besides the message string) relevant to the exception.


 

Comments (18)

  1. Kalpesh says:

    KC, thanks for replying.

    I still have a question. What is the purpose of "ApplicationException"?

    The .net fx documentation says, "If you are designing an application that needs to create its own exceptions, derive from the ApplicationException class"

  2. A small bit on Exceptions. I can’temphasize enough: "As described in more detail later, catching System.Exception

  3. Trackback from DotNetKicks.com

  4. Jeff Stong says:

    Now that you’re aware of the design guidlines for exception throwing, and know which exceptions types…

  5. Kyralessa says:

    I like the little red X’s and green checkboxes.  They make the Do’s and Don’ts stick out a lot better.  Any chance of getting those in the MSDN2 documentation?  🙂

  6. Kyralessa,

    I contacted the MSDN people and they said they will look into it.

    Thanks for the feedabck.

  7. David Levine says:

    I believe there are issues with using custom exceptions that need more guidelines.

    When the exception thrown will be entirely consumed within the assembly that defines the custom exception then the guidelines above are sufficient (and then it really does not need to be marked as serializable unless explicitly serialized internally)

    When the custom exception may be thrown across process boundaries, and especially across machine boundaries, then there are other factors that should be considered. The problem is that the entity that receives the exception must have a copy of the assembly that defines the exception to be able to deserialize the exception. If it does not have a copy of the assembly, or if it has the wrong version of the assembly, then a different exception will be thrown by the runtime when it attempts and fails to deserialize the custom exception. This is worse then not using a custom exception because the client will get a misleading exception type to deal with, and any extra data associated with the custom exception will be lost.  The ability of an application to correctly interpret and respond to exceptions is of vital importance, and in this situation the abiity to deal meaningfully with custom exception type will be lost or degraded.

    If this can happen then the custom exception should be mapped at the boundary of the source to an exception defined in a system assembly so that it can be easily consumed. (I feel this is such an important issue that the runtime should provide an automatic mapping service at the receiver to remove this burden from the sender, but that’s a different issue).

    In general, when custom exceptions are exposed to external clients across process and machine boundaries then consideration must be given to the deployment and versioning issues of the assembly in which the custom exception is defined, and if clients will have access to this assembly. If these issues cannot be adequately addressed then consider using standard exceptions instead.

  8. Krish says:

    Hi Krzysztof,


    As described in more detail later, catching System.Exception is nearly always the wrong thing to do as well.

    I agree with you in principle, but the .NET mechanism of not being able to declare exceptions thrown by methods does lead to a lot of headaches. For example, if you properly document your code, you can ensure (not 100% safe) that many people are aware of the possible exceptions that can raised by the code you are calling into. If you don’t document your code, well, too bad, and the only other options are to look at the source code (if available) or use ILDasm or some other tool to figure out possible exceptions that can be thrown by the code you’re calling into.

    I’m not getting into a checked/unchecked exception debate here, but if a method CAN but does [not] have to declare the exceptions that it raises, [and] you add a compiler switch to raise a warning if not declared, that would help methods self-document themselves.

    Thoughts?

    -krish

    For example:

    // legal, but a special compiler flag could raise a warning
    public void M1(Boolean flag)
    {
    if (flag)
    throw new MyException1();
    else
    throw new MyException2();
    }

    // legal, but the special compiler flag could raise a warning for Exception2
    public void M1(Boolean flag) throws Exception1
    {
    if (flag)
    throw new MyException1();
    else
    throw new MyException2();
    }

    // legal, no warnings when using special compiler flag
    public void M1(Boolean flag) throws Exception1, Exception2
    {
    if (flag)
    throw new MyException1();
    else
    throw new MyException2();
    }

    In theory, this is similar to Java exceptions that extend from java.lang.RuntimeException (an unchecked exception). Methods that raise the RuntimeException (or sub-classes thereof) can – but do not have to – declare exceptions in the method signature.

  9. Krish, we are thinking about improving the current exceptions story. I agree that what we have today can be way better. We want to strike the right balance without being too rigid (checked exceptions) and too ad-hoc where as you note it’s very difficult to write very robust code.

  10. Nigel Armstrong says:

    I also think that you need to alter this exception story slightly. Specificially this item:

    "Do not create and throw new exceptions just to have ‘your team’s’ exception."

    If you imagine having a typical multitier application, which has a UI that is going to use data by calling on a method from a data access class. You might have several data access classes (one for XML, one for databases, etc)…the XML reading and writing data access class might have to deal with XmlExceptions, the database one a SqlException. But it shouldn’t propagate these back to the UI – instead in my view, your data acess team should create an exception class "the data access team’s exception" – eg DataAccessException that is used for all of these classes to report back to the UI that an exception occured. Of course, this exception should set the InnerException correctly so that developers can drill down and see what they did wrong…if you don’t do that, then you end up with an "abstraction leak" where the UI has to be aware of what the specific data access technology is being used at that time…

  11. Krzysztof, thanks for this guidance, which is invaluable, but I’ve realised that there is at least one scenario where the guidelines contradict each other. That scenario is when an error condition arises that does NOT match any of the predefined system exceptions, but at the same time does NOT require any specific programmatic handling.

    When would such a scenario occur? Well, for me it happens when I’m baking a fundamental, low-level assumption about an internal data structure into my code, and I cannot imagine any circumstances that would cause the assumption to be violated.

    Now I’m acutely conscious that although I can’t IMAGINE any troublesome circumstances, that does NOT mean that they don’t exist. And quite often, the code path that would exist IF my assumption were violated is just staring me right in the face.

    Equally often, Bad Things happen on that code path, and unless I’m in an absolutely performance-critical section, I don’t feel good about just ignoring it. Obviously I can use Asserts to verify that it will never execute while I’m developing, but what if the assumption gets violated in a release build? My Asserts are useless. It seems negligent NOT to throw an exception, even though I’m pretty sure it will never actually be raised.

    But I don’t know how to handle that exception programatically. The circumstances that brought it about are, as I said, unimaginable and therefore by definition I don’t know what to do in those circumstances. I just want my code to stop running before it wreaks havoc.

    If I’m following the guidelines strictly then I can’t throw Exception, SystemException or ApplicationException. But the assumption that’s been violated is often unrelated to parameters or anything else I can articulate using the builtin fine-grained exceptions. Previously I might have created a NameOfMyProjectException and thrown that, just to indicate very clearly that, if the exception is ever raised, then it’s a bug. But that’s against the guidelines too.

    Any thoughts on what to do in these circumstances?

  12. Nigel, I think the way to address the abstraction leak is to describe exceptions that can be handled in the contract of the abstraction. Having DataAccessTeamException does not help much: it’s not an exception that can be handled, at least not more than System.Exception is.

    John, yes, there are cases where we don’t have a good general "it’s a bug" exception in the framework, but such cases are extremly rare. In such cases, I would add a new exception, but I would not name it after the team; I would name it so it describes how it’s different from existing exceptions in the framework (i.e. describe why you could not throw any of the existing ones). But as I said, it’s extremly rare. For example, InvalidOperationException might be a good candidate in cases you described.

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

  14. Sundar Rajan says:

    Hi,

    In your framework design book you mention that one should avoid throwing COMException since it is for the CLR to throw it.

    We are in the process of writing a data access layer, which is in C#. However the first client of this layer is a COM/C++ application. In order to propagate exceptions from .Net to COM (so that the COM application gets a HResult) we are using COMException. Is this a resonable violation of the guideline, or is there a better way to communicate exception across interop boundaries?

    Thanks

    -Sundar

  15. nblog says:

    Jakoś ostatnio często wpadają mi do rąk różne teksty o wyjątkach w .Net. Z tego co da się zauważyć, to

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