Defining error codes

cerror handlingerrorslibraries

We are designing an error-handling framework for a cryptographic library written in C.

The approach we are taking is that relatively few errors are propagated back to the user since on most occasions he cannot do anything about them (notable exception: parameter validation errors. Of course we tell the user if a call succeeded. We'd also create log entries for fault investigation.) In short, just because we have an error condition somewhere inside the library it does not mean that we need a public (user-facing) error code for it.

Also, from past experience we think that a significant portion of error conditions that require error codes pertain to very narrow areas of code, it is often the case that a particular error is only returned from (or set by) one function.

Therefore we would like to define error codes at the lowest possible level. For example, if only one function can return/set a particular error code we would define that code in the .h file where the function prototype is declared.

However, in such case we might get error code conflicts – the same error code value for different errors. Can someone please propose an elegant approach to avoid such conflicts? Or at the very least an automated mechanism to check for such conflicts (which can then be fixed by hand)?

Best Answer

we would define that code in the .h file where the function prototype is declared.

I would recommend against this as you don't have anything to reduce the space for potential numbering conflicts.

Instead, use a common header file providing error codes for both your client and server applications. As an alternative, the client file can simply be a subset of the server error code file.

Within the common header file, each major area of your application will get a sequence of error numbers. And from within each major area, you can assign subsets of error code ranges to the modules within that layer.

For example, say you have a session manager, a business logic, and a database access layers in your server application. Your header file might be broken down like this:

/*
 * our error.h file 
 */
/* Common Errors 0 - 99 */
#define SUCCESS 0
#define GENERIC_ERROR 1
/* Session Manager errors 100 - 199 */
    /* Login issues 100 - 110 */
#define BAD_PASSWORD 101
    /* session issues 120 - 125 */
    /* other session issues 150 - 162 */
#define SOMETHING_ELSE 150
/* Business logic errors 200 - 299 */

/* Database errors 300 - 399 */
#define INVALID_CONNECTION 300

The advantage of this approach is it cuts down the overall space where conflicts in numbering can occur. Potential conflict is limited to the areas where two or more developers are working simultaneously.

By having a common error header file, your developers can check out the file as needed to create a new error and make sure they are claiming an unused number at that point in time.


Potential pitfalls with this approach are when developers don't frequently commit their local copy of the error header. But that's a process issue that will usually work its way out once those developers have had to deal with it a few times. And since they are referring to the error number through the #define it's not that big of a change.

Another pitfall is when you misjudge the range required for a layer or sub-module. The easiest way to avoid this is to pre-allocate really large ranges for each layer and sub-module. And from there, you should be able to trim from one sub-module in order to give greater range to another. And there's nothing that says the ranges have to be contiguous.

Related Topic