thodg/libgit2/docs/error-handling.md

Tag

Download

Error reporting in libgit2

Libgit2 tries to follow the POSIX style: functions return an int value with 0 (zero) indicating success and negative values indicating an error. There are specific negative error codes for each “expected failure” (e.g. GIT_ENOTFOUND for files that take a path which might be missing) and a generic error code (-1) for all critical or non-specific failures (e.g. running out of memory or system corruption).

When a negative value is returned, an error message is also set. The message can be accessed via the giterr_last function which will return a pointer to a git_error structure containing the error message text and the class of error (i.e. what part of the library generated the error).

For instance: An object lookup by SHA prefix (git_object_lookup_prefix) has two expected failure cases: the SHA is not found at all which returns GIT_ENOTFOUND or the SHA prefix is ambiguous (i.e. two or more objects share the prefix) which returns GIT_EAMBIGUOUS. There are any number of critical failures (such as a packfile being corrupted, a loose object having the wrong access permissions, etc.) all of which will return -1. When the object lookup is successful, it will return 0.

If libgit2 was compiled with threads enabled (-DTHREADSAFE=ON when using CMake), then the error message will be kept in thread-local storage, so it will not be modified by other threads. If threads are not enabled, then the error message is in global data.

All of the error return codes, the git_error type, the error access functions, and the error classes are defined in include/git2/errors.h. See the documentation there for details on the APIs for accessing, clearing, and even setting error codes.

When writing libgit2 code, please be smart and conservative when returning error codes. Functions usually have a maximum of two or three “expected errors” and in most cases only one. If you feel there are more possible expected error scenarios, then the API you are writing may be at too high a level for core libgit2.

Example usage

When using libgit2, you will typically capture the return value from functions using an int variable and check to see if it is negative. When that happens, you can, if you wish, look at the specific value or look at the error message that was generated.

{
    git_repository *repo;
    int error = git_repository_open(&repo, "path/to/repo");

    if (error < 0) {
        fprintf(stderr, "Could not open repository: %s\n", giterr_last()->message);
        exit(1);
    }

    ... use `repo` here ...

    git_repository_free(repo); /* void function - no error return code */
}

Some of the error return values do have meaning. Optionally, you can look at the specific error values to decide what to do.

{
    git_repository *repo;
    const char *path = "path/to/repo";
    int error = git_repository_open(&repo, path);

    if (error < 0) {
        if (error == GIT_ENOTFOUND)
            fprintf(stderr, "Could not find repository at path '%s'\n", path);
        else
            fprintf(stderr, "Unable to open repository: %s\n",
                giterr_last()->message);
        exit(1);
    }

    ... happy ...
}

Some of the higher-level language bindings may use a range of information from libgit2 to convert error return codes into exceptions, including the specific error return codes and even the class of error and the error message returned by giterr_last, but the full range of that logic is beyond the scope of this document.

Example internal implementation

Internally, libgit2 detects error scenarios, records error messages, and returns error values. Errors from low-level functions are generally passed upwards (unless the higher level can either handle the error or wants to translate the error into something more meaningful).

int git_repository_open(git_repository **repository, const char *path)
{
    /* perform some logic to open the repository */
    if (p_exists(path) < 0) {
        giterr_set(GITERR_REPOSITORY, "The path '%s' doesn't exist", path);
        return GIT_ENOTFOUND;
    }

    ...
}

The public error API

Deviations from the standard

There are some public functions that do not return int values. There are two primary cases:

There are a few other exceptions to these rules here and there in the library, but those are extremely rare and should probably be converted over to other to more standard patterns for usage. Feel free to open issues pointing these out.

There are some known bugs in the library where some functions may return a negative value but not set an error message and some other functions may return zero (no error) and yet leave an error message set. Please report these cases as issues and they will be fixed. In the meanwhile, please code defensively, checking that the return value of giterr_last is not NULL before using it, and not relying on giterr_last to return NULL when a function returns 0 for success.

The internal error API

Writing error messages

Here are some guidelines when writing error messages:

General guidelines for error reporting


Source

Download