Error Code Paradigms


At some point when I was reading the comments on the “Exceptions as repackaged error codes” post, I had an epiphany (it’s reflected in the comments to that thread but I wanted to give it more visibility).

I’m sure it’s just an indication of just how slow my mind is working these days, but I just realized that in all the “error code” vs. “exception” discussions that seem to go on interminably, there are two UNRELATED issues being discussed.

The first is about error semantics – what information do you hand to the caller about what failed.  The second is about error propogation – how do you report the failure to the caller.

It’s critical for any discussion about error handling to keep these two issues separate, because it’s really easy to commingle them.  And when you commingle them, you get confusion.

Consider the following example classes (cribbed in part from the previous post):

class Win32WrapperException
{
    // Returns a handle to the open file.  If an error occurs, it throws an object derived from
    // System.Exception that describes the failure.
    HANDLE OpenException(LPCWSTR FileName)
    {
        HANDLE fileHandle;
        fileHandle = CreateFile(FileName, xxxx);
        if (fileHandle == INVALID_HANDLE_VALUE)
        {
            throw (System.Exception(String.Format("Error opening {0}: {1}", FileName, GetLastError());
        }

    };
    // Returns a handle to the open file.  If an error occurs, it throws the Win32 error code that describes the failure.
    HANDLE OpenError(LPCWSTR FileName)
    {
        HANDLE fileHandle;
        fileHandle = CreateFile(FileName, xxxx);
        if (fileHandle == INVALID_HANDLE_VALUE)
        {
            throw (GetLastError());
        }

    };
};

class Win32WrapperError
{
    // Returns either NULL if the file was successfully opened or an object derived from System.Exception on failure.
    System.Exception OpenException(LPCWSTR FileName, OUT HANDLE *FileHandle)
    {
        *FileHandle = CreateFile(FileName, xxxx);
        if (*FileHandle == INVALID_HANDLE_VALUE)
        {
            return new System.Exception(String.Format("Error opening {0}: {1}", FileName, GetLastError()));
        }
        else
        {
            return NULL;
        }

    };
    // Returns either NO_ERROR if the file was successfully opened or a Win32 error code describing the failure.
    DWORD OpenError(LPCWSTR FileName, OUT HANDLE *FileHandle)
    {
        *FileHandle = CreateFile(FileName, xxxx);
        if (&FileHandle == INVALID_HANDLE_VALUE)
        {
            return GetLastError();
        }
        else
        {
            return NO_ERROR;
        }
    };
};

I fleshed out the example from yesterday and broke it into two classes to more clearly show what I’m talking about.  I have two classes that perform the same operation.  Win32WrapperException is an example of a class that solves the “How do I report a failure to the caller” problem by throwing exceptions.  Win32WrapperError is an example that solves the “How do I report a failure to the caller” problem by returning an error code.

Within each class are two different methods, each of which solves the “What information do I return to the caller” problem – one returns a simple numeric error code, the other returns a structure that describes the error.  I used System.Exception as the error structure, but it could have just as easily been an IErrorInfo class, or any one of a bazillion other ways of reporting errors to callers.

But looking at these examples, it’s not clear which is better.  If you believe that reporting errors by exceptions is better than reporting by error codes, is Win32WrapperException::OpenError better than Win32WrapperError::OpenException?  Why? 

If you believe that reporting  errors by error codes is better, then is CWin32WrapperError::OpenError better than CWin32WrapperError::OpenException?  Why?

When you look at the problem in this light (as two unrelated problems), it allows you to look at the “exceptions vs. error codes” debate in a rather different light.  Many (most?) of the arguments that I’ve read in favor of exceptions as an error propagation mechanism  concentrate on the additional information that the exception carries along with it.  But those arguments ignore the fact that it’s totally feasible (and in fact reasonable) to define an error code based system that provides the caller with exactly the same level of information that is provided by exception.

These two problems are equally important when dealing with errors.  The mechanism for error propagation has critical ramifications for all aspects of engineering – choosing one form of error propagation over another can literally alter the fundamental design of a system.

And the error semantic mechanism provides critical information for diagnosability – both for developers and for customers.  Everyone HATES seeing a message box with nothing but “Access Denied” and no additional context.

 

And yes, before people complain, I recognize that none of the common error code returning APIs today provide the same quality of error semantics that System.Exception does as first class information – the error return information is normally hidden in a relatively unsophisticated scalar value.  I’m just saying that if you’re going to enter into a discussion of error codes vs. exceptions, from a philosophical point of view, then you need to recognize that there are two related problems that are being discussed, and differentiate between these two. 

In other words, are you advocating exceptions over error codes because you like how they solve the “what information do I return to the caller?” problem, or are you advocating them because you like how they solve the “how do I report errors?” problem?

Similarly, are you denigrating exceptions because you don’t like their solution to the “how do I report errors?” problem and ignoring the “what information do I return to the caller?” problem?

Just some food for thought.

Comments (33)

  1. Anonymous says:

    In many ways I was unknowingly dancing around this very observation with my post about the difference between throwing the return code and throwing the return code’s string representation. My view was from the consumer standpoint which was more about what was being reported and not how it was being reported. It wasn’t until your post that I realized this myself.

  2. Anonymous says:

    With the new longhorn system being more .Net based hopefully we’ll see more innerExceptions describing exactly what the real reason was 5 levels down in the API.

  3. Anonymous says:

    What you say is correct, but it’s not the only separation of errors that should be considered.

    There are two types of fault – those that are expected and those that aren’t.

    Expected errors will be handled (whether by exception or error code) close to the point of the call that failed.

    Unexpected errors have to return the system to a stable state and then get ready to try again – how far back they have to go in order to do this depends on nature of the application and the fault (it could be a network server that drops this request and readies itself for the next, or it could be a Windows app that kills the process and allows the user to restart it).

    The key thing IMHO is that it is not the OpenFile function that should be making the choice – it is the caller that should.

    More specifically, a library should never make the choice.

  4. Anonymous says:

    I agree with much of what Yaytay said. My first experiance with exceptions was with MFC’s CFile class that just loved to throw exceptions in the case of opening files. In my application, file open errors are expected. Thus trying to use the exception system made the software much harder to write than using just simple fopen.

  5. Yaytay, IMHO, that’s a valid point but unrelated to my discussion.

    The problem (as you mention) is that it’s impossible for a DLL (or other library) to know what the intentions of the caller are. All it can do is to specify its contract and push the problem up to the caller.

  6. Anonymous says:

    Even compared to "rich" error returns, throwing exceptions gives you more (debugging) information – namely the stack traceback.

    If you return your error information, the information about the original error location is usually lost. And, if you return the same error object further up the call stack, the the call stack is lost.

    On the other hand, Exceptions automatically maintain a stack traceback that can be reported at the catching site for debugging purposes.

    Of course, you could also maintain a stack traceback with error returns, but you would have to maintain it manually, which is clumsy and error-prone.

  7. oefe: How do you get the stack traceback?

    Exceptions don’t "automatically" maintain a stack trace unless you have the debugger break when someone throws an exception?

    How do you differentiate between the real failure and the false positives? Because if the system’s using exceptions exclusively you’re likely to have false positives (I know, I’ve been burned by this before).

    I think you’re once again confusing a property of System.Exception (stack backtraces generated at the point of object construction) and a property of exception handling as a propogation paradigm.

  8. Anonymous says:

    Just because everyone hates to see "access denied" with no context does not make it a bad thing.

    Error messages can easily report too much information to a caller, particularly if the caller is hostile. I would much rather my web server report "access denied" to a malicious hacker than "access denied to SQL server database foo on machine bar, ip address blah, while executing query abc".

    My favourite example of this, which fortunately was fixed long before .NET v1.0 shipped to customers, was an exception message that basically said "you do not have permission to determine the name of directory "c:blahwhatever" " — great, thanks for letting me know!

    My least favourite example of this, because it was my fault, was a serious security hole in VBScript — http://blogs.msdn.com/ericlippert/archive/2003/09/24/53089.aspx

  9. You’re absolutely right Eric – Sometimes it’s NOT good to return the full context that’s contained in the exception, especially if there’s a bad guy on the other side.

    On the other hand, from a usability expert, the "Access is Denied." message box ranks well up there in the "messages that suck" department.

  10. Anonymous says:

    Good post, but I think it’s a bit odd to say "I recognize that none of the common error code returning APIs today provide the same quality of error semantics that System.Exception does". You’ve essentially staked out a middle ground that exists only in theory. Nobody who advocates for return codes over exceptions offers this as an alternative.

    I’m not denying it’s a vast improvement over returning an int – it is – but there must be some reason you never see this in practice. Is there a practical downside that makes this less attractive than it seems? There’s some sort of runtime penalty (in C, at least).

  11. Anonymous says:

    Throwing exceptions is a fun and wonderful way to write code. It’s catching them that sucks. Give me error codes over try/catch any day.

  12. Anonymous says:

    Throwing exceptions is a much better way to code. Catching them is what sucks. Gimme error codes over try/catch any day.

  13. Anonymous says:

    I think the point is propagation of exceptions, and the fact that if the developer doesn’t catch them or handle them in some way, he’ll see it anyway(at runtime 🙂 ??? ). The code doesn’t continue executing.

    Having a system with error code, no matter how descriptive, it’s totaly up to the developer to investigate the error code for success or failure. In this case the code keeps executing and this can lead to some major bugs which may not show themselfs right away.

  14. Anonymous says:

    Eric: Using a counter example where too much information was leaked is _not_ a justification to build APIs that report genercic error messages. I think this is one pervert effect of the security push inside ms. We end up finding excuses for sloppy error reporting for the sake of "security". The matter of fact is that "Access is denied" (actually, my scapegoat is usually E_FAIL: Unspecified Error) is not a useful error message. ASP.NET implemented a great workaround to the information leakage problem: Exception messages aren’t sent back to the client browser by default. That’s a much more imaginative solution than saying "Report generic error message, so an eventual attacker can’t do anything with it". If you’re not convinced, let me use a counter counter example: just think what our life as programmers would be if our favorite c++/c# compiler would merely report "Invalid type" when compiling a program containing a type mismatch error: no filename, no line #, no type name. Justification: The compiler error reporting mechanism had to be trimmed down for security reasons because we didn’t want attackers to know the name/location of our source files. Every programmer in the world would say this is absurd. But it seems we have no problem putting in place a similar system for end users.

    Larry: I agree there is a difference between what error is returned and how it is returned. I also agree error codes/exceptions can be equally used (and equally misused) for both.

    In general, however, the perversion of the discussions I’ve seen about exceptions vs error codes is that, even if they are considered equivalent in "theory", they are compared against their current/actual implementations. For example, I’ve heard many times "throwing an exception is more expensive than returning an error code". Of course, that is correct if an error code is a DWORD, but if you set up a system where error codes are rich, well, returning an error code is a matter of returning the equivalent of System.Exception. Is that really less expensive? (Probably a little bit, but certainly not orders of magnitude as we’ve been told before).

    I have a personal preference for an exception based mecanism, because the intent is often more explicit. In your "OpenException" example, it’s not cleat what the value of "FileHandle" is supposed to be when the return value is not null. You annotate it with an "OUT" modifier, but it’s purely documentation. We’ve all run into bugs where we don’t initialize the return value when we return S_OK or where the caller expects us to set the output param to a "resonable" default value even when the function fails. An exception based system eliminates this class of bugs. I think it also tend to make code cleaner (no noise about bubling up error codes to the callers), and it forces programmers to adopt sound programming practices (RAII in C++: no goto’s!, try/finally in c#).

    That said, I am always surprised how much time spent is arguing about exception vs error codes, and i have yet to see any improvement in API’s published by MS wrt to rich error reporting. Most API’s (I suspect Longhorn will not be much different, now that managed code usage has been reduced) are HRESULT based, and don’t even bother supporing IErrorInfo. If I had the choice between a rich error reporting mechanism vs an poorly designed exception based one, i would chose the former one any day.

    Could we for once start a discussion about defining a rich error reporting mechanism (exception based or not) that could be used and exposed by Windows APIs. That would benefit customers much more than philosophical discussions.

    – How do we support localization for a rich error reporting mechanism?

    – How do we make sure programs don’t take dependencies on message strings?

    – How do we make sure it work accross APIS (Win32, COM, etc.)?

    – How do we extend existing APIs to suport the new model?

  15. Anonymous says:

    I have to agree with Andrei. Coming from a Delphi background, exceptions feel very natural to me.

    It takes the same amount of work to differentiate between expected and unexpected exceptions – reading the docs or code – but at least you can be sure you’ll be stopped right in the place where you failed. The RTL makes sure you do.

    Not something you can say for error codes.

  16. Anonymous says:

    Renaud,

    If you catch an exception in the same function that threw it, the "cost" of throwing the exception is very low (I believe most compilers will generate a jump instruction directly to the correct exception handler).

    But, this isn’t the typical/expected case.

    If you catch the exception in a different function, the costs start to rise dramatically. The act of throwing the exception triggers a software interupt. The operating system handles the interupt and attempts to find a handler for the exception. I’m not incredibly certain of all of the operations involved, but I think it would be reasonable to assume that stack unwinding is required along with logic to determine if the handler being checked can actually handle the exception (the logic is whatever the compiler generates to do the type checking/comparisons needed by the catch statement).

    I believe that you can also factor in the cost of registering and unregistering each handler at the beginning and end of each try/catch block — this isn’t a significant cost, but it is something added that error codes don’t need to deal with.

    It IS an expensive operation. Now the question is, when an error condition occurs, do you care about perf?

    This is assuming that we’re talking about C++ here…I believe the performance implications are less severe in many cases for managed code (though I haven’t read much about how it works under the hood).

    One of the biggest problems with using exceptions in C++ is the lack of any sort of "finally" block. You can sort of work around the limitation by creating classes designed to do some sort of cleanup when destroyed, but it isn’t natural and has its own set of problems … (the biggest being, IMO, that you can’t control the order in which the objects are destroyed).

    For code that I write, I tend to use error codes for "problems" that I write code to handle; ie: the user never knows or sees the problem.

    For cases where an error must surface to a user, I typically use an exception instead of an error code + extended information.

    The biggest problems with using some extra API to report additional error information (GetLastError, IErrorInfo, etc) is that the information you wish to retrieve may be unexpectedly trashed before you can get to it. A contrived example:

    MyFunc(_bstr_t("hello world"));

    In this case, we’ll say that MyFunc fails and sets a last error of access denied. However, when I make a call to GetLastError(), I’ll get ERROR_SUCCESS. Why? Because the destructor for the _bstr_t calls SysFreeString, which sets the last error to ERROR_SUCCESS when the string is successfully deallocated.

    This is an exceptionally bad problem as your error information can be wiped as a side effect of the internal workings of other code you know nothing about, which makes life "interesting"…:)

  17. Renaud,

    Exceptions are always an order of magnitude more expensive than returning an error code (regardless of the complexity of the error code).

    The thing about exception codes is that you need to walk the stack looking for an exception handler for the type of the object being thrown (or …). That stack walk takes time (potentially tens of thousands of instructions on non x86 architectures). Returning an error code means putting a value in a register.

  18. Anonymous says:

    I doubt that you can hold the additional impact exceptions have over error codes as detrimental. The application would make a similar impact if it attempted to diagnose the same information – and have a much more difficult time doing it.

    That said I almost always use exceptions – it seems cleaner to me, and I don’t have to worry about someone above me failing to deal with it. If they fail to deal with an exception, it moves up the stack to the next level.

    However, having to determine the possible list of exceptions and attempt to bring the application back under control – well, that’s the classic argument. "An error code is used to return an expected failure; an exception is used to indicate the system has failed completely." I’ve seen several places where the use of both is recommended, and the only purpose of catching an exception is to clean up as best as possible.

  19. Anonymous says:

    "How do you get the stack traceback?

    Exceptions don’t "automatically" maintain a stack trace unless you have the debugger break when someone throws an exception?"

    Well, for my applications, I have a default exception handler set up for the app domain that takes any exception that I’ve failed to anticipate that I’d need to handle, then formats and dumps the whole thing, stack trace and all – and if it’s one of "my" exceptions rather than a system one, extra information regarding thread, assembly version, etc. – straight into my bug-tracking system via its associated web service.

    I catch a lot of unanticipated issues that way, and it’s certainly a lot easier to implement than putting something similar everywhere I might get an error code.

  20. Anonymous says:

    One other bit that comes up for me in the exceptions vs. error codes discussion:

    How forcefully do you want to express the error?

    In other words, what if I’m building a component that will be used by other developers, and when something bad happens, I _really_ need for execution to stop. (situation where ignoring the error code would be really bad)

    Now maybe I’m just more used to reading exception related code, but it seems easier to miss someone failing to check a return code than when someone intentionally silences an exception:

    {



    DoSomethingCritical(); // uh oh



    }

    try

    {

    DoSomethingCritical()

    }

    catch

    {

    }

    When DoStuff throws, it is going to interrupt execution. When I am reading the code, and I see an unchecked catch (one that doesn’t specify an exception type), it says **DANGER**. When I see that it doesn’t do anything with the exception, it says *DANGER DANGER DANGER*

    But then, maybe to a person who is used to reading error-code based work, the failure to check a return value is equally glaring.

    I suppose that it could all depend upon perception. The error codes just seem to be (to me) a lot more subtle. Maybe exceptions have greater value when you really, really need for someone to take heed that something bad has happened?

  21. Anonymous says:

    Larry: An error code is not about putting a value in a register if you have to generate the equivalent of System.Exception. You have to create an object on the heap, capture the callstack, generate the error message string (probably from a resource dll if it’s localized). Anyway, the point of my post was not about performance, it was about rich error reporting system.

    mirobin: At least you’re honest with the drawbacks of your "convention" (which is the one used by Windows in many cases). An OOB error reporting mechanism (GetLastError, GetErrorInfo) is terribly complicated to get right and I’ve seen _many_ bugs with it. Have you ever seen entries in the Event Log such as: "The application "blah" encountered an error: Operation successfully completed". Guess what happened?

    Also, if you micro benchmark a system that throws thousands of exceptions/sec, I can believe an exception based system is slower. But in a well behaved application, there are perf advantage of using exceptions:

    1. less branching: In my experience, with error code based paradigms, you end up testing every single function call for success/failures, and return from the funtion (if you use RAII properly) or perform a "goto end".

    2. In the c++ case, it is most efficient to write this:

    MyObject o = MyFunc();

    than

    MyObject o;

    if (failed(hr = MyFunc(o)) return hr;

    when "MyObject" is a non trivial type.

    Also, you example illustrates another point: Even with error code based system, programmers tend to uses libraries which are exceptions based (_bstr_t, ATL collections, STL, boost) and "ignore" the fact they can throw exceptions (atl, for example, usually only throws on out of memory or invalid indices conditions). Most modern c++ libraries use exceptions to some extend. If you decide to use an error code based paradigm, you put yourself in a position where you have

    1. to avoid re-using any of those libraries and rewrite everything from scratch, and use c-style array collections, for example.

    2. "ignore" the fact they can throw

    3. add try/catches all over the place in your code

    4. wrap them in wrapper class that do the try/catch for you.

    I don’t like any of those solutions. From what I have seen, MS programmers tend to use solution #1 for most of their components. Maybe that explains the amount of buffers overruns we see.

    I just found this article on the net, btw. I have never used boost, so i don’t know how rich their exception reporting is:

    http://www.boost.org/more/generic_exception_safety.html

  22. Anonymous says:

    Two comments.

    1. In the Error version, the return value is never what you actually called the function for; it’s always a return code. This makes seeing where values change much harder; you have to scan within the function parameters as well as down the left side of the code. I tend to lean towards the opinion that "ref" and "out" parameters usually indicate badly written or broken code for this reason.

    2. This is related to the first point: the Error version makes the C# "using" and VB.Net "With" constructs impossible, thus bloating code and reducing readability.

  23. Eric,

    For #1, That’s the error return information you’re talking about – not the propogation information. And exceptions are about propogation. Which once again proves my point – you’re talking about exceptions but referring to error return information.

    For #2, THAT’S about propogation. And you’re right, using and with aren’t as useful in an error code propogation model

  24. Anonymous says:

    No, I am talking about propogation. Exception propogation doesn’t require you to restructure your code and move variable changes all over the place. Consider:

    int myCalculatedValue = 0;



    int result = CalculateMyValue(param1, param2, ref myCalculatedValue);

    if (result != RESULT_SUCCESS)

    {



    }

    vs.

    int myCalculatedValue = 0;



    myCalculatedValue = CalculateMyValue(param1, param2);

    In the second example, it is blindingly obvious even if you don’t have the source for CalculateMyValue that myCalculatedValue is being changed. In the first example, it’s not. Not using exceptions to *propogate* the error information (whether you "throw new int(5)" or "throw new Exception(lotsofinformation)") helps to conceal where the value could be changed, which can cause problems IME.

  25. Ok, point taken. In other words, returning errors forces the contract for an API to be more obscure, thus forcing you to understand the use of all the parameters to an API while using exceptions allows the contract to be more obvious?

  26. Anonymous says:

    Yes, that’s what I mean. 🙂

  27. Anonymous says:

    Also, I suppose it’s not strictly true that using error code propogation makes using and with impossible; it’s just a great deal more cumbersome to write code using them.

    MyObject foo;

    int result = SomeFunctionThatReturnsAFoo(params);

    if (result == ERROR_SUCCESS)

    {

    using (foo)

    {



    }

    }

    else

    {



    }

  28. Anonymous says:

    Um, obviously I meant to pass an "out foo" to that function. 😛

  29. Anonymous says:

    I can see in Eric’s examples the benefit of using return values for output parameters.

    Assignment makes output parameters obvious. Since we only get one function return value in most languages (Matlab is an exception), this works well for functions with a single output parameter. This also works for data types that don’t have error representations.

    We still have to deal with the problem of communicating error information to the caller, both for flow-control and reporting/propagation…

    Are we going to package the information in an exception and let the run-time walk the handler list with it?

    Are we going to use some other out-of-band mechanism such as SetLastError or by placing richer error information in a thread-level queue?

    Hmmm…

    How about defining error representation in the object itself (e.g. floating point has well-defined values for NAN, INF, etc.). This allows the caller to use assignment to be explicit about output parameters while getting the benefit of rich error information embodied in the returned object itself.

    db_connection_t conn = db_connect_odbc(…);

    if (!conn.is_open())

    {

    // error information is available, if you want it

    }

    // conn.execute propagates the error information to the cursor if

    // the connection’s not open, or if the execution failed

    db_cursor_t cursor = conn.execute(“select * from clients”);

    Excel uses this type of error propagation in its calculations, allowing a downstream error to propagate through a chain of formulas. This same technique can be applied in a procedural environment, with the cost of adding error representation (and related behavior) to key classes.

    The error information is available to the caller without forcing the caller to use try/catch, and bombing their entire process if they don’t catch a simple runtime failure.

    The error representation stuff is what I’ve been using for the past few years and have been able to get low-level error messages (e.g. invalid A/D channel number) to propagate all the way up to UI displays.

    Anyway, my $0.02.

  30. Anonymous says:

    I just got this in the system log:

    > Generate Activation Context failed for

    > [censored……].exe.Manifest.

    > Reference error message: The operation

    > completed successfully.

    This is a very famous error message, having been laughed at for at least a decade prior to Visual Studio 2005 beta 2.

    Now the question is, what is the exception version of this error? How do you throw a non-exception? How do you catch a non-exception?

  31. Anonymous says:

    Note that the throw keyword[1] takes an expression, not necessarily an object deriving from System.Expression.

    You can throw anything; similarly you can catch anything.

    AFAIK (not tested!)

    try

    {

    int x = 5;

    throw x;

    }

    catch (int y)

    {

    }

    is entirely legal.

    [1]http://msdn.microsoft.com/library/default.asp?url=/library/en-us/csref/html/vclrfthethrowstatement.asp

  32. Anonymous says:

    I’ve written about this very topic here:

    http://www.arkestra.demon.co.uk/errors_return_or_ex.html

    You’re correct that propagation and information content are largely two separate issues. However, there is a subtle interaction. Since exceptions propagate automatically, the intervening layers of code don’t need to know about the type of the error being propagated. By contrast, when you’re passing error values back, the code has to at least know the base class of what it’s dealing with. So in general, this makes it easier to propagate complex error information through a large multi-section system via exceptions. This is particularly handy in C++, since that’s a language without a common base object.

    The basic guidelines I came up with are:

    1) Is there even a choice?

    – If Your Language Doesn’t Support Exceptions, Use Return Values

    – If You Can’t Return A Value, Throw An Exception

    2) How far will the information have to travel?

    – If The Error May Not Propagate At All, Use A Return Value

    – If The Error Will Probably Propagate Many Levels, Use An Exception

    3) Other guidelines:

    – If Exceptions Are Too Slow, Use Return Values

    – Exceptions Are Better For Complex Errors (Especially In C++)

    – Be Prepared To Use Both Error Forms

    I think it’s easy to go overboard in favour of one or the other: for instance, people making APIs (Raymond Chen for instance) often lean towards return values while people making frameworks often lean towards exceptions.

  33. Anonymous says:

    It strikes me that this whole discussion treats "errors" as a homogenous concept. Many of the examples, however, indicate that it is not. A grouping may sched some new light on the two issues about error semantics – what information do you hand to the caller about what failed – and error propagation – how do you report the failure to the caller.

    Seen from the perspective of a development team, let "external errors" be errors that are outside of their control, and "internal errors" be errors done (and controlled) by themselves.

    Examples of external errors would include user input errors, communication link errors, and file system errors. These are "expected" errors. They appear in the postcondition of the corresponding contracts, but not in their preconditions. Being part of the postconditions, they demand explicit handling. The target audience for an error message is the end user, who needs an intelligible, textual message. The end user may indicate corrective actions.

    Examples of internal errors would include invalid input parameters to function calls, and attempts to open non-existent files. These are "unexpected" (or at least unwanted) errors that should have been avoided (see TesterDoer by Krzysztof Cwalina, http://blogs.msdn.com/kcwalina/archive/2005/03/16/396787.aspx ). These situations are basically attempts to perform illegal or impossible calls, in short precondition voilations. They appear in the preconditions of the corresponding contracts, but not in their posctonditions. The target audience for an error message is the developer team, who needs technical information to trace and remove the "bug". Generally speaking, corrective actions are not appropriate.

    For external errors, error codes may be appropriate, due to their explicit nature in the code as they propagate to the surface.

    For internal errors, undeclared and uncaught exceptions may be appropriate due to their implicit nature (remember, they correspond to a precondition violation and are NOT specifiec in the contract postconditions). Also, the strategy for handling these internal errors may change during the lifetime if the project. This is possible since they are not part of the contract postconditions.