Chapter 9
HDF5 Error Handling

1. Introduction

The HDF5 Library provides an error reporting mechanism for both the library itself and user application programs. It can trace errors through function stack and error information like file name, function name, line number, and error description.

Section 2 of this chapter discusses the HDF5 error handling programming model.

Section 3 presents summaries of HDF5’s error handling functions.

Section 4 discusses the basic error concepts, such as error stack, error record, and error message, and the related API functions. These concepts and functions are sufficient for application programs to trace errors inside the HDF5 Library.

Section 5 talks about the advanced concepts of error class and error stack handle, and the related functions. With these concepts and functions, an application library or program using the HDF5 Library can have its own error report blended with HDF5’s error report.

Starting with Release 1.8, we have a new set of Error Handling API functions. For the purpose of backward compatibility with version 1.6 and before, we still keep the old API functions, H5Epush, H5Eprint, H5Ewalk, H5Eclear, H5Eget_auto, H5Eset_auto. These functions do not have the error stack as parameter. The library allows them to operate on the default error stack. Users do not have to change their code to catch up with the new Error API but are encouraged to do so.

The old API is similar to functionality discussed in Section 4. The functionality discussed in Section 5, the ability of allowing applications to add their own error records, is the library new design for the Error API.

2. Programming Model

3. Error Handling (H5E) Function Summaries


C Function
F90 Function
Purpose
H5Eauto_is_v2
(none)
Determines the type of error stack.
H5Eclear
h5eclear_f
Clears the error stack for the current thread.
H5Eclear_stack
(none)
Clears the error stack for the current thread.
H5Eclose_msg
(none)
Closes an error message identifier.
H5Eclose_stack
(none)
Closes object handle for error stack.
H5Ecreate_msg
(none)
Add major error message to an error class.
H5Eget_auto
(none)
Returns the current settings for the automatic error stack traversal function and its data.
H5Eget_auto2
(none)
Returns the current settings for the automatic error stack traversal function and its data.
H5Eget_class_name
(none)
Retrieves error class name.
H5Eget_current_stack
(none)
Registers the current error stack.
H5Eget_major
h5eget_major_f
Returns a character string describing an error specified by a major error number.
H5Eget_minor
h5eget_minor_f
Returns a character string describing an error specified by a minor error number.
H5Eget_msg
(none)
Retrieves an error message.
H5Eget_num
(none)
Retrieves the number of error messages in an error stack.
H5Epop
(none)
Deletes specified number of error messages from the error stack.
H5Eprint
h5eprint_f
Prints the error stack in a default manner.
H5Eprint2
(none)
Prints the error stack in a default manner.
H5Epush
(none)
Pushes new error record onto error stack.
H5Epush2
(none)
Pushes a new error record onto the error stack.
H5Eregister_class
(none)
Registers a client library or application program to the HDF5 error API.
H5Eset_auto
h5eset_auto_f
Turns automatic error printing on or off.
H5Eset_auto2
(none)
Turns automatic error printing on or off.
H5Eset_current_stack
(none)
Replaces the current error stack.
H5Eunregister_class
(none)
Removes an error class.
H5Ewalk
(none)
Walks the error stack for the current thread, calling a specified function.
H5Ewalk2
(none)
Walks the error stack for the current thread, calling a specified function.

4. Basic Error Handling Operations

4.1 Introduction

Let us first try to understand the error stack. An error stack is a collection of error records. Error records can be pushed onto or popped off the error stack. By default, when an error occurs deep within the HDF5 Library, an error record is pushed onto an error stack and that function returns a failure indication. Its caller detects the failure, pushes another record onto the stack, and returns a failure indication. This continues until the API function called by the application returns a failure indication. The next API function being called will reset the error stack. All HDF5 Library error records belong to the same error class (explained in Section 5).

4.2 Error Stack and Error Message

In normal circumstances, an error causes the stack to be printed on the standard error stream automatically. This automatic error stack is the library’s default stack. For all the functions in this section, whenever an error stack ID is needed as a parameter, H5E_DEFAULT can be used to indicate the library’s default stack. The first error record of the error stack, number "#000", is produced by the API function itself and is usually sufficient to indicate to the application what went wrong.

Example: An Error Report

If an application calls H5Tclose on a predefined data type then the following message is printed on the standard error stream. This is a simple error that has only one component, the API function; other errors may have many components.

HDF5-DIAG: Error detected in HDF5 (1.6.4) thread 0.
  #000: H5T.c line 462 in H5Tclose(): predefined datatype
    major: Function argument
    minor: Bad value

From the example we can see, an error record has a major message and a minor message. A major message generally indicates where the error happens. The location can be data set, data space, or heap, etc. A minor message explains further details of the error, for example, “unable to open file”. Another specific detail about the error can be found at the end of the first line of each error record. This error description is usually added by the library designer to tell what exactly goes wrong. In the example above, the “predefined datatype” is an error description.

4.3 Print and Clear Error Stack

Besides the automatic error report, the error stack can also be printed and cleared by the functions H5Eprint2() and H5Eclear_stack(). If an application wishes to make explicit calls to H5Eprint2() to print the error stack, the automatic printing should be turned off to prevent error messages from being displayed twice (see H5Eset_auto2() below).

herr_t H5Eprint2(hid_t error_stack, FILE * stream)

Prints the error stack specified by error_stack on the specified stream, stream. Even if the error stack is empty, a one-line message will be printed like this if the error is in HDF5 Library:
HDF5-DIAG: Error detected in HDF5 Library version: 1.5.62 thread 0.

herr_t H5Eclear_stack(hid_t error_stack)

H5Eclear clears the error stack specified by error_stack. H5E_DEFAULT can be passed in to clear the current error stack. The current stack is also cleared whenever an API function is called, with certain exceptions (for instance, H5Eprint2()).

4.4 Mute Error Stack

Sometimes an application calls a function for the sake of its return value, fully expecting the function to fail; sometimes the application wants to call H5Eprint2() explicitly. In these situations, it would be misleading if an error message were still automatically printed. Using the H5Eset_auto2() function can control the automatic printing of error messages:

herr_t H5Eset_auto2(hid_t error_stack, H5E_auto2_t func, void *client_data)
Turns on or off automatic printing of errors for the error stack specified by error_stack. When turned on (non-null func pointer), any API function which returns an error indication will first call func, passing it client_data as an argument. When the library is first initialized the auto printing function is set to H5Eprint() (cast appropriately) and client_data is the standard error stream pointer, stderr.

herr_t H5Eget_auto2(hid_t error_stack, H5E_auto2_t * func, void **client_data )
Returns the current settings for the automatic error stack traversal function, func, and its data, client_data. Either (or both) arguments may be null in which case the value is not returned.

Example: Error Control

An application can temporarily turn off error messages while "probing" a function.

/* Save old error handler */
herr_t (*old_func)(void*);
void *old_client_data;

H5Eget_auto2(error_stack, &old_func, &old_client_data);

/* Turn off error handling */
H5Eset_auto2(error_stack, NULL, NULL);

/* Probe. Likely to fail, but that's okay */
status = H5Fopen (......);

/* Restore previous error handler */
H5Eset_auto2(error_stack, old_func, old_client_data);

Or automatic printing can be disabled altogether and error messages can be explicitly printed.

/* Turn off error handling permanently */
H5Eset_auto2(error_stack, NULL, NULL);
/* If failure, print error message */
if (H5Fopen (....)<0) {
    H5Eprint2(H5E_DEFAULT, stderr);
    exit (1);
}

4.5 Customized Printing of Error Stack

The application is allowed to define an automatic error traversal function other than the default H5Eprint2(). For instance, one can define a function that prints a simple, one-line error message to the standard error stream and then exits.

Example: Simple Messages

The application defines a function to print a simple error message to the standard error stream.

herr_t
my_hdf5_error_handler(void *unused)
{
   fprintf (stderr, "An HDF5 error was detected. Bye.\n");
   exit (1);
}

The function is installed as the error handler by saying

H5Eset_auto2(H5E_DEFAULT, my_hdf5_error_handler, NULL);

4.6 Walk Through the Error Stack

The H5Eprint2() function is actually just a wrapper around the more complex H5Ewalk2() function which traverses an error stack and calls a user-defined function for each member of the stack.

herr_t H5Ewalk2(hid_t err_stack, H5E_direction_t direction, H5E_walk2_t func, void *client_data)
The error stack err_stack is traversed and func is called for each member of the stack. Its arguments are an integer sequence number beginning at zero (regardless of direction), and the client_data pointer. If direction is H5E_WALK_UPWARD then traversal begins at the inner-most function that detected the error and concludes with the API function. The opposite order is H5E_WALK_DOWNWARD.

typedef herr_t (*H5E_walk2_t)(unsigned n, H5E_error2_t *eptr, void * client_data)
An error stack traversal callback function takes three arguments: n is a sequence number beginning at zero for each traversal, eptr is a pointer to an error stack member, and client_data is the same pointer passed to H5Ewalk2().

typedef struct {
    hid_t	cls_id;
    hid_t       maj_num;
    hid_t       min_num;
    unsigned    line;
    const char  *func_name;
    const char  *file_name;
    const char  *desc;
} H5E_error2_t;

The maj_num and min_num are major and minor error IDs, func_name is the name of the function where the error was detected, file_name and line locate the error within the HDF5 Library source code, and desc points to a description of the error.

Example of callback function:
The following callback function serves as an example for a user-defined callback function.

#define MSG_SIZE       64

herr_t
custom_print_cb(unsigned n, const H5E_error2_t *err_desc, void* client_data)
{
    FILE		*stream  = (FILE *)client_data;
    char                maj[MSG_SIZE];
    char                min[MSG_SIZE];
    char                cls[MSG_SIZE];
    const int		indent = 4;

    /* Get descriptions for the major and minor error numbers */
    if(H5Eget_class_name(err_desc->cls_id, cls, MSG_SIZE)<0)
        TEST_ERROR;

    if(H5Eget_msg(err_desc->maj_num, NULL, maj, MSG_SIZE)<0)
        TEST_ERROR;

    if(H5Eget_msg(err_desc->min_num, NULL, min, MSG_SIZE)<0)
        TEST_ERROR;

    fprintf (stream, "%*serror #%03d: %s in %s(): line %u\n",
	     indent, "", n, err_desc->file_name,
	     err_desc->func_name, err_desc->line);
    fprintf (stream, "%*sclass: %s\n", indent*2, "", cls);
    fprintf (stream, "%*smajor: %s\n", indent*2, "", maj);
    fprintf (stream, "%*sminor: %s\n", indent*2, "", min);

    return 0;

  error:
    return -1;
}

5. Advanced Error Handling Operations

5.1 Introduction

Section 4 discusses the basic error handling operations of the library. In that section, all the error records on the error stack are from the library itself. In this section, we are going to introduce the operations that allow an application program to push its own error records onto the error stack once it declares an error class for its own through the HDF5’s Error API.

Example: An Error Report

An error report shows both the library’s error record and the application’s error records.

Error Test-DIAG: Error detected in Error Program (1.0) thread 8192:
  #000: ../../hdf5/test/error_test.c line 468 in main(): Error test failed
    major: Error in test
    minor: Error in subroutine
  #001: ../../hdf5/test/error_test.c line 150 in test_error(): H5Dwrite failed 
      as supposed to
    major: Error in IO
    minor: Error in H5Dwrite
HDF5-DIAG: Error detected in HDF5 (1.7.5) thread 8192:
  #002: ../../hdf5/src/H5Dio.c line 420 in H5Dwrite(): not a dataset
    major: Invalid arguments to routine
    minor: Inappropriate type 

Notice in the line above error record #002 in the example, the starting phrase is HDF5. This is the error class name of the HDF5 Library. All of the library’s error messages (major and minor) are in this default error class. The Error Test in the beginning of the line above error record #000 is the name of the application’s error class. The first two error records, #000 and #001, are from application’s error class.

In definition, an error class is a group of major and minor error messages for certain library (the HDF5 or an application library built on top of the HDF5 Library) or application program. Error class can be registered for an application library or program through the HDF5 Error API. Major messages and minor messages can be defined in an error class. An application will have object handles for error class, major and minor messages for further operation.

5.2 Register Application with Error API

These functions can be used to register or unregister an error class, create or close error messages, or do query on an error class and error message:

hid_t H5Eregister_class(const char* cls_name, const char* lib_name, const char* version)
Registers an error class to HDF5 Library so that the application library or program can report errors together with HDF5 Library.

hid_t H5Ecreate_msg(hid_t class, H5E_type_t msg_type, const char* mesg)
Adds an error message to an error class defined by application library or program. The error message can be either major or minor which is indicated by parameter msg_type.

ssize_t H5Eget_class_name(hid_t class_id, char* name, size_t size)
Retrieves the name of the error class specified by the class ID.

ssize_t H5Eget_msg(hid_t mesg_id, H5E_type_t* mesg_type, char* mesg, size_t size)
Retrieves the error message including its length and type.

herr_t H5Eclose_msg(hid_t mesg_id)
Closes an error message.

herr_t H5Eunregister_class(hid_t class_id)
Removes an error class from the error API.

Example: Error Class and Its Message

An application creates an error class and error messages.

/* Create an error class */
class_id = H5Eregister_class(ERR_CLS_NAME, PROG_NAME, PROG_VERS);

/* Retrieve class name */
H5Eget_class_name(class_id, cls_name, cls_size); 

/* Create a major error message in the class */
maj_id = H5Ecreate_msg(class_id, H5E_MAJOR, "... ...");

/* Create a minor error message in the class */
min_id = H5Ecreate_msg(class_id, H5E_MINOR, "... ...");

Application closes error messages and un-registers error class.

H5Eclose_msg(maj_id);
H5Eclose_msg(min_id);
H5Eunregister_class(class_id);

5.3 Push Application Error Message Onto Error Stack

An application can push error records onto or pop error records off the error stack, just as the library does internally. An error stack can be registered, and an object handle can be returned to the application, so that the application can manipulate a registered error stack.

hid_t H5Eget_current_stack(void)
Registers the current error stack, returns an object handle, and clears the current error stack. An empty error stack will also be assigned an ID.

herr_t H5Eset_current_stack(hid_t error_stack)
Replaces the current error stack with another error stack specified by error_stack, clears the current error stack. The object handle error_stack is closed after this function call.

herr_t H5Epush2(hid_t error_stack, const char* file, const char* func, unsigned line, hid_t cls_id, hid_t major_id, hid_t minor_id, const char* desc, ... )
Pushes a new error record onto the error stack for the current thread.

herr_t H5Epop(hid_t error_stack, size_t count)
Deletes some error messages from the error stack.

int H5Eget_num(hid_t error_stack)
Retrieves the number of error records from an error stack.

herr_t H5Eclear_stack(hid_t error_stack)
Clears the error stack.

herr_t H5Eclose_stack(hid_t error_stack)
Closes the object handle for an error stack and releases its resources.

Example: Error Stack

An application pushes error record onto default error stack.

/* Make call to HDF5 I/O routine */
if((dset_id=H5Dopen(file_id, dset_name))<0)
{
	/* Push client error onto error stack */
	H5Epush2(H5E_DEFAULT,__FILE__,FUNC,__LINE__,cls_id,CLIENT_ERR_MAJ_IO,
		CLIENT_ERR_MINOR_OPEN,"H5Dopen failed");

	/* Indicate error occurred in function */
	return(0);
}

The application registers current error stack and creates an object handle to avoid another HDF5 function clearing the error stack.

if(H5Dwrite(dset_id, mem_type_id, mem_space_id, file_space_id, dset_xfer_plist_id, buf)<0)
{
	/* Push client error onto error stack */
	H5Epush2(H5E_DEFAULT,__FILE__,FUNC,__LINE__,cls_id,CLIENT_ERR_MAJ_IO,
		    CLIENT_ERR_MINOR_HDF5,"H5Dwrite failed");

	/* Preserve the error stack by assigning an object handle to it */
    error_stack = H5Eget_current_stack();
                
	/* Close dataset */
	H5Dclose(dset_id);
               
    /* Replace the current error stack with the preserved one */
    H5Eset_current_stack(error_stack);

	Return(0);
}