Error Reporting
Error Reporting
GLib provides a standard method of reporting errors from a called function
to the calling code. (This is the same problem solved by exceptions in other
languages.) It’s important to understand that this method is both a data
type (the GError
struct) and a set of rules. If you use
GError
incorrectly, then your code will not properly interoperate with
other code that uses GError
, and users of your API will probably get
confused. In most cases, using GError
is preferred over numeric error
codes, but there are situations where numeric error codes are useful for performance.
First and foremost: GError
should only be used to report recoverable
runtime errors, never to report programming errors. If the programmer has
screwed up, then you should use g_warning()
, g_return_if_fail()
,
g_assert()
, g_error()
, or some similar facility. (Incidentally, remember
that the g_error()
function should only be used for programming errors, it
should not be used to print any error reportable via GError
.)
Examples of recoverable runtime errors are “file not found” or “failed to
parse input.” Examples of programming errors are “NULL passed to strcmp()
”
or “attempted to free the same pointer twice.” These two kinds of errors are
fundamentally different: runtime errors should be handled or reported to the
user, programming errors should be eliminated by fixing the bug in the
program. This is why most functions in GLib and GTK do not use the GError
facility.
Functions that can fail take a return location for a GError
as their last
argument. On error, a new GError
instance will be allocated and returned
to the caller via this argument. For example:
gboolean g_file_get_contents (const char *filename,
char **contents,
gsize *length,
GError **error);
If you pass a non-NULL
value for the error
argument, it should
point to a location where an error can be placed. For example:
char *contents;
GError *err = NULL;
g_file_get_contents ("foo.txt", &contents, NULL, &err);
g_assert ((contents == NULL && err != NULL) || (contents != NULL && err == NULL));
if (err != NULL)
{
// Report error to user, and free error
g_assert (contents == NULL);
fprintf (stderr, "Unable to read file: %s\n", err->message);
g_error_free (err);
}
else
{
// Use file contents
g_assert (contents != NULL);
}
Note that err != NULL
in this example is a reliable indicator of whether
g_file_get_contents()
failed. Additionally, g_file_get_contents()
returns a boolean which indicates whether it was successful.
Because g_file_get_contents()
returns FALSE
on failure, if you
are only interested in whether it failed and don’t need to display
an error message, you can pass NULL
for the error
argument:
if (g_file_get_contents ("foo.txt", &contents, NULL, NULL)) // ignore errors
// no error occurred
;
else
// error
;
The GError
object contains three fields: domain
indicates the module the
error-reporting function is located in, code
indicates the specific error
that occurred, and message
is a user-readable error message with as many
details as possible. Several functions are provided to deal with an error
received from a called function: g_error_matches()
returns TRUE
if the
error matches a given domain and code, g_propagate_error()
copies an error
into an error location (so the calling function will receive it), and
g_clear_error()
clears an error location by freeing the error and
resetting the location to NULL
. To display an error to the user, simply
display the message
, perhaps along with additional context known only to
the calling function (the file being opened, or whatever - though in the
g_file_get_contents()
case, the message
already contains a filename).
Since error messages may be displayed to the user, they need to be valid
UTF-8 (all GTK widgets expect text to be UTF-8). Keep this in mind in
particular when formatting error messages with filenames, which are in the
‘filename encoding’, and need to be turned into UTF-8 using
g_filename_to_utf8()
, g_filename_display_name()
or
g_utf8_make_valid()
.
Note, however, that many error messages are too technical to display to the
user in an application, so prefer to use g_error_matches()
to categorize
errors from called functions, and build an appropriate error message for the
context within your application. Error messages from a GError
are more
appropriate to be printed in system logs or on the command line. They are
typically translated.
Reporting errors
When implementing a function that can report errors, the basic tool is
g_set_error()
. Typically, if a fatal error occurs you want to
g_set_error()
, then return immediately. g_set_error()
does nothing if
the error location passed to it is NULL
. Here’s an example:
int
foo_open_file (GError **error)
{
int fd;
int saved_errno;
g_return_val_if_fail (error == NULL || *error == NULL, -1);
fd = open ("file.txt", O_RDONLY);
saved_errno = errno;
if (fd < 0)
{
g_set_error (error,
FOO_ERROR, // error domain
FOO_ERROR_BLAH, // error code
"Failed to open file: %s", // error message format string
g_strerror (saved_errno));
return -1;
}
else
return fd;
}
Things are somewhat more complicated if you yourself call another function
that can report a GError
. If the sub-function indicates fatal errors in
some way other than reporting a GError
, such as by returning TRUE
on
success, you can simply do the following:
gboolean
my_function_that_can_fail (GError **err)
{
g_return_val_if_fail (err == NULL || *err == NULL, FALSE);
if (!sub_function_that_can_fail (err))
{
// assert that error was set by the sub-function
g_assert (err == NULL || *err != NULL);
return FALSE;
}
// otherwise continue, no error occurred
g_assert (err == NULL || *err == NULL);
}
If the sub-function does not indicate errors other than by reporting a
GError
(or if its return value does not reliably indicate errors) you need
to create a temporary GError
since the passed-in one may be NULL
.
g_propagate_error()
is intended for use in this case.
gboolean
my_function_that_can_fail (GError **err)
{
GError *tmp_error;
g_return_val_if_fail (err == NULL || *err == NULL, FALSE);
tmp_error = NULL;
sub_function_that_can_fail (&tmp_error);
if (tmp_error != NULL)
{
// store tmp_error in err, if err != NULL,
// otherwise call g_error_free() on tmp_error
g_propagate_error (err, tmp_error);
return FALSE;
}
// otherwise continue, no error occurred
}
Error pileups are always a bug. For example, this code is incorrect:
gboolean
my_function_that_can_fail (GError **err)
{
GError *tmp_error;
g_return_val_if_fail (err == NULL || *err == NULL, FALSE);
tmp_error = NULL;
sub_function_that_can_fail (&tmp_error);
other_function_that_can_fail (&tmp_error);
if (tmp_error != NULL)
{
g_propagate_error (err, tmp_error);
return FALSE;
}
}
tmp_error
should be checked immediately after
sub_function_that_can_fail()
, and either cleared or propagated upward. The
rule is: after each error, you must either handle the error, or return it to
the calling function.
Note that passing NULL
for the error location is the equivalent of
handling an error by always doing nothing about it. So the following code is
fine, assuming errors in sub_function_that_can_fail()
are not fatal to
my_function_that_can_fail()
:
gboolean
my_function_that_can_fail (GError **err)
{
GError *tmp_error;
g_return_val_if_fail (err == NULL || *err == NULL, FALSE);
sub_function_that_can_fail (NULL); // ignore errors
tmp_error = NULL;
other_function_that_can_fail (&tmp_error);
if (tmp_error != NULL)
{
g_propagate_error (err, tmp_error);
return FALSE;
}
}
Note that passing NULL
for the error location ignores errors; it’s
equivalent to:
try { sub_function_that_can_fail (); } catch (...) {}
in C++. It does not mean to leave errors unhandled; it means to handle them by doing nothing.
Error domains
Error domains and codes are conventionally named as follows:
- The error domain is called
<NAMESPACE>_<MODULE>_ERROR
, for exampleG_SPAWN_ERROR
orG_THREAD_ERROR
: “`c #define G_SPAWN_ERROR g_spawn_error_quark ()
G_DEFINE_QUARK (g-spawn-error-quark, g_spawn_error) “`
-
The quark function for the error domain is called
<namespace>_<module>_error_quark
, for exampleg_spawn_error_quark()
org_thread_error_quark()
. -
The error codes are in an enumeration called
<Namespace><Module>Error
; for example,GThreadError
orGSpawnError
. -
Members of the error code enumeration are called
<NAMESPACE>_<MODULE>_ERROR_<CODE>
, for exampleG_SPAWN_ERROR_FORK
orG_THREAD_ERROR_AGAIN
. -
If there’s a “generic” or “unknown” error code for unrecoverable errors it doesn’t make sense to distinguish with specific codes, it should be called
<NAMESPACE>_<MODULE>_ERROR_FAILED
, for exampleG_SPAWN_ERROR_FAILED
. In the case of error code enumerations that may be extended in future releases, you should generally not handle this error code explicitly, but should instead treat any unrecognized error code as equivalent toFAILED
.
Comparison of GError
and traditional error handling
GError
has several advantages over traditional numeric error codes:
importantly, tools like gobject-introspection
understand GError
s and convert them to exceptions in bindings; the message
includes more information than just a code; and use of a domain helps
prevent misinterpretation of error codes.
GError
has disadvantages though: it requires a memory allocation, and
formatting the error message string has a performance overhead. This makes
it unsuitable for use in retry loops where errors are a common case, rather
than being unusual. For example, using G_IO_ERROR_WOULD_BLOCK
means
hitting these overheads in the normal control flow. String formatting
overhead can be eliminated by using g_set_error_literal()
in some cases.
These performance issues can be compounded if a function wraps the GError
s
returned by the functions it calls: this multiplies the number of
allocations and string formatting operations. This can be partially
mitigated by using g_prefix_error()
.
Rules for use of GError
Summary of rules for use of GError
:
-
Do not report programming errors via
GError
. -
The last argument of a function that returns an error should be a location where a
GError
can be placed (i.e.GError **error
). IfGError
is used with varargs, theGError**
should be the last argument before the...
. -
The caller may pass
NULL
for theGError**
if they are not interested in details of the exact error that occurred. -
If
NULL
is passed for theGError**
argument, then errors should not be returned to the caller, but your function should still abort and return if an error occurs. That is, control flow should not be affected by whether the caller wants to get aGError
. -
If a
GError
is reported, then your function by definition had a fatal failure and did not complete whatever it was supposed to do. If the failure was not fatal, then you handled it and you should not report it. If it was fatal, then you must report it and discontinue whatever you were doing immediately. -
If a
GError
is reported, out parameters are not guaranteed to be set to any defined value. -
A
GError*
must be initialized toNULL
before passing its address to a function that can report errors. -
GError
structs must not be stack-allocated. -
“Piling up” errors is always a bug. That is, if you assign a new
GError
to aGError*
that is non-NULL
, thus overwriting the previous error, it indicates that you should have aborted the operation instead of continuing. If you were able to continue, you should have cleared the previous error withg_clear_error()
.g_set_error()
will complain if you pile up errors. -
By convention, if you return a boolean value indicating success then
TRUE
means success andFALSE
means failure. Avoid creating functions which have a boolean return value and aGError
parameter, but where the boolean does something other than signal whether theGError
is set. Among other problems, it requires C callers to allocate a temporary error. Instead, provide agboolean *
out parameter. There are functions in GLib itself such asg_key_file_has_key()
that are hard to use because of this. IfFALSE
is returned, the error must be set to a non-NULL
value. One exception to this is that in situations that are already considered to be undefined behaviour (such as when ag_return_val_if_fail()
check fails), the error need not be set. Instead of checking separately whether the error is set, callers should ensure that they do not provoke undefined behaviour, then assume that the error will be set on failure. -
A
NULL
return value is also frequently used to mean that an error occurred. You should make clear in your documentation whetherNULL
is a valid return value in non-error cases; ifNULL
is a valid value, then users must check whether an error was returned to see if the function succeeded. -
When implementing a function that can report errors, you may want to add a check at the top of your function that the error return location is either
NULL
or contains aNULL
error (e.g.g_return_if_fail (error == NULL || *error == NULL);
).
Extended GError
Domains
Since GLib 2.68 it is possible to extend the GError
type. This is
done with the G_DEFINE_EXTENDED_ERROR()
macro. To create an
extended GError
type do something like this in the header file:
typedef enum
{
MY_ERROR_BAD_REQUEST,
} MyError;
#define MY_ERROR (my_error_quark ())
GQuark my_error_quark (void);
int
my_error_get_parse_error_id (GError *error);
const char *
my_error_get_bad_request_details (GError *error);
and in the implementation:
typedef struct
{
int parse_error_id;
char *bad_request_details;
} MyErrorPrivate;
static void
my_error_private_init (MyErrorPrivate *priv)
{
priv->parse_error_id = -1;
// No need to set priv->bad_request_details to NULL,
// the struct is initialized with zeros.
}
static void
my_error_private_copy (const MyErrorPrivate *src_priv, MyErrorPrivate *dest_priv)
{
dest_priv->parse_error_id = src_priv->parse_error_id;
dest_priv->bad_request_details = g_strdup (src_priv->bad_request_details);
}
static void
my_error_private_clear (MyErrorPrivate *priv)
{
g_free (priv->bad_request_details);
}
// This defines the my_error_get_private and my_error_quark functions.
G_DEFINE_EXTENDED_ERROR (MyError, my_error)
int
my_error_get_parse_error_id (GError *error)
{
MyErrorPrivate *priv = my_error_get_private (error);
g_return_val_if_fail (priv != NULL, -1);
return priv->parse_error_id;
}
const char *
my_error_get_bad_request_details (GError *error)
{
MyErrorPrivate *priv = my_error_get_private (error);
g_return_val_if_fail (priv != NULL, NULL);
g_return_val_if_fail (error->code != MY_ERROR_BAD_REQUEST, NULL);
return priv->bad_request_details;
}
static void
my_error_set_bad_request (GError **error,
const char *reason,
int error_id,
const char *details)
{
MyErrorPrivate *priv;
g_set_error (error, MY_ERROR, MY_ERROR_BAD_REQUEST, "Invalid request: %s", reason);
if (error != NULL && *error != NULL)
{
priv = my_error_get_private (error);
g_return_val_if_fail (priv != NULL, NULL);
priv->parse_error_id = error_id;
priv->bad_request_details = g_strdup (details);
}
}
An example of use of the error could be:
gboolean
send_request (GBytes *request, GError **error)
{
ParseFailedStatus *failure = validate_request (request);
if (failure != NULL)
{
my_error_set_bad_request (error, failure->reason, failure->error_id, failure->details);
parse_failed_status_free (failure);
return FALSE;
}
return send_one (request, error);
}
Please note that if you are a library author and your library exposes an
existing error domain, then you can’t make this error domain an extended one
without breaking ABI. This is because earlier it was possible to create an
error with this error domain on the stack and then copy it with
g_error_copy()
. If the new version of your library makes the error domain
an extended one, then g_error_copy()
called by code that allocated the
error on the stack will try to copy more data than it used to, which will
lead to undefined behavior. You must not stack-allocate errors with an
extended error domain, and it is bad practice to stack-allocate any other
GError
s.
Extended error domains in unloadable plugins/modules are not supported.