Exceptions as repackaged error codes

One of the comments on my philosopy of error codes post from last week indicated that all the problems I listed with error codes were solved by exceptions.

The thing that I think the writer missed is that CLR (and Java) exceptions serve two totally different design patterns w.r.t. error handling.

You see, CLR exceptions solve both the "how do I report an error" problem, AND the "what information should be contained in my error report" problem.  The first part of the solution has to do with the asynchronous nature of exceptions - any statement can potentially throw an exception, and a caller is expected to catch the exception.  The second part is about what information is carried along with the error information.

IMHO, the System.Exception object is just another kind of error code object - it's functionally equivalent to an HRESULT combined with the IErrorInfo interface.  It's job is to provide sufficient context to the caller that the caller can determine some kind of reasonable behavior based on the error.

In fact, you could almost consider an exception hierarchy based off of System.Exception as a modern implementation of an X.400/x.500 OM error structure (X.400/X.500 OM errors are complex nested structures that described the source of the error, suggested recovery modes, etc).

The interesting thing about x.400/x.500 error codes is that they were sufficiently complicated that they were almost completely unusable.  Most people who manipulated them took the highly complex data structure and mapped it to a simple error code and operated off of that error code.  Why?  Because it was simpler - checking against a particular error code number was far easier than parsing the OM_error structure.

The good news for the "System.Exception as an uber error code" is that it's relatively easy to determine what kind of error failed from the strong type information that the CLR provides, which means that the "deconstruct the rich information into a simpler version" pattern I just mentioned isn't likely to happen.

But you should never believe that "exceptions" somehow solve the "how do I return sufficient information to the caller of my API" problem - exceptions per se do not, even though an object hierarchy derived from System.Exception has the potential of solving it.  But the "throw an exception to handle your errors" design pattern doesn't.

As a trivial counter example, consider the following C++ class (I'm just typing this in, don't expect this to work):class Win32Wrapper{   HANDLE Open(LPCWSTR FileName)   {        HANDLE fileHandle;        fileHandle = CreateFile(FileName, xxxx);        if (fileHandle == INVALID_HANDLE_VALUE)        {            throw (GetLastError());        }    };   DWORD OpenError(LPCWSTR FileName, OUT HANDLE *FileHandle)   {        *FileHandle = CreateFile(FileName, xxxx);        if (&FileHandle == INVALID_HANDLE_VALUE)        {            return GetLastError();        }        else        {            return NO_ERROR;        }    };};

This rather poorly implemented class has two methods.  One of them uses exception handling for error propagation, the other returns the error code.  The thing is that as far as being able to determine corrective action, the two functions are totally equivalent.  Neither of them give the caller any information about what to do about the error.

So claiming that the "throw an exception as a way of reporting an error" paradigm somehow solves the "what should I do with this error" problem is naive.

Comments

  • Anonymous
    May 31, 2005
    The comment has been removed
  • Anonymous
    May 31, 2005
    I believe there is no general solution for the "what should I do with this error" problem. It simply depends on the context in which a given function is called which is normally unknown to the function.

    If Open() fails in an interactive application you show an error message and ask the user to enter a new filename. If it fails in a service you are pretty much done.

    The function call says WHAT should be done. The code inside defines HOW it is done. But there is no way to tell WHY it is done. You just cannot see the big picture from inside.
  • Anonymous
    May 31, 2005
    The comment has been removed
  • Anonymous
    May 31, 2005
    The comment has been removed
  • Anonymous
    June 01, 2005
    I did say that this example was a silly example.

    It was intended to show that "exceptions" alone don't solve the error handling problem.

    That's because there are TWO error handling issues - one is "what happened and what should I do with it", and the other is "how do I know something happened".

    Btw, as for CreateFile failures being exceptions, they are in the CLR - opening a file throws a System.FileNotFoundException (I don't have MSDN to check that this is the right exception)
  • Anonymous
    June 01, 2005
    The comment has been removed
  • Anonymous
    June 01, 2005
    Oops. You are indeed correct. I didn't actually lose all of the information, I threw an IOException with a message rather than just a string. A better way of doing things would be (in java, again my C++ is pretty rubbish):

    class ErrorConverter
    {
    static Exception convert(int code)
    {
    switch (code){
    case FILE_NOT_FOUND:
    return new FileNotFoundException(getLocalizableMessage(code));

    etc.

    And to use:

    throw(ErrorConverter.convert(GetLastError));

    Java (and I assume C#) provides a mechanism for providing localizable error messages in exceptions.
    That way you get a specific error type, the type hierarchy allows you to treat groups of exceptions the same (FileNotFoundException is an IOException), and you get a human readable error message.

    (The example above will give you a slightly off stacktrace but there are ways around that)
  • Anonymous
    June 01, 2005
    The comment has been removed
  • Anonymous
    June 01, 2005
    There are two sides to "what should I do with this error": what the program does with the error and what the user/administrator/developer does with the error.

    I agree that exceptions don't give the program any more help than an error code, but information about what the program was doing and what, e.g. file, it was acting on can be invaluable to solving configuration problems.
  • Anonymous
    June 01, 2005
    The comment has been removed
  • Anonymous
    June 01, 2005
    Asd: Almost. To be totally specific, I'm saying that there are two UNRELATED issues that people comingle. The first (exception handling) solves the "how do I report an error to an application" problem. The second (System.Exception) solves the "what information do I report to the user" problem.

    But it's critical to realize that these are two UNRELATED problems.
  • Anonymous
    June 01, 2005
    The difference though is that exceptions have the potential to provide arbitarily rich error information whereas simple error codes don't. Just because there's no way to force programmers to actually provide that richness doesn't invalidate the whole method anymore than programmers who always return E_OUTOFMEMORY for any error condition invalidates the benefits of HRESULTs.

    One could also argue that in this trivial example the damage has already been done because CreateFile is limited in it's ability to return rich error context.
  • Anonymous
    June 01, 2005
    Mike, you're still comingling two unrelated concepts. You're talking about exceptions as if they were "system.exception". They're not. Exceptions solve the "how do I RETURN error information to the caller" problem. They don't solve the "what information should I tell the caller about the failure".

    Error codes are a solution to the latter problem, as is the System.Exception class, as is the OM error code mentioned above. Exceptions as a paradigm don't solve the problem per se. You can write code that uses exceptions to report error that is just as bad as returning an error code - that's what the example above is intended to show.

    You need BOTH exceptions AND a rigid structure of what can be thrown before you get a viable replacement.
  • Anonymous
    June 01, 2005
    Larry,

    Doesn't the Exception type and/or the message solve the "what information do I tell the caller" problem?
  • Anonymous
    June 01, 2005
    Scott: Yes it does. But "exceptions" don't.

    You could just as easily have a System.Exception be the NULL/Non Null return value from a function and have the same effect.