Search code examples
goerror-handling

What's the difference between errors.Wrapf(), errors.Errorf(), and fmt.Errorf()?


What's the difference between these three functions from Go's standard packages:

Wrapf returns an error annotating err with a stack trace at the point Wrapf is called, and the format specifier. If err is nil, Wrapf returns nil.

Errorf formats according to a format specifier and returns the string as a value that satisfies error. Errorf also records the stack trace at the point it was called.

Errorf formats according to a format specifier and returns the string as a value that satisfies error.

If the format specifier includes a %w verb with an error operand, the returned error will implement an Unwrap method returning the operand. It is invalid to include more than one %w verb or to supply it with an operand that does not implement the error interface. The %w verb is otherwise a synonym for %v.

When one should be used instead of others?


Solution

  • First, a correction:

    github.com/pkg/errors is not part of the standard library! The standard errors package has a much smaller API.

    That said, github.com/pkg/errors is very popular, and was* maintained by some prominent Gophers. It is, however, largely (though not completely**) obsoleted by Go 1.13's extended error support.

    Understanding the difference between those three functions requires a bit of a history lesson. (I go into a bit more detail on this history in this video.)

    Prior to Go 1.13, there was no officially-recognized way to "wrap" errors. github.com/pkg/errors filled this gap with the Wrap and Wrapf methods. This allowed wrapping an error with additional context (including a stack trace), while retaining the original error in pristine form.

    When Go 1.13 was in development, github.com/pkg/errors was used to influence the new API, but the final version differed slightly. Rather than Wrap and Wrapf methods, they decided to extend the fmt.Errorf method with a new %w verb, which would perform error wrapping for you.

    This means that the following bits of code are roughly** equivalent:

        import "github.com/pkg/errors"
    
        /* snip */
    
        return errors.Wrapf(err, "bad things")
    
        // +build go1.13
    
        import "fmt"
    
        /* snip */
        return fmt.Errorf("bad things: %w", err)
    

    When Go 1.13 came out, and the %w verb was added to fmt.Errorf, github.com/pkg/errors followed suit and added the same support, so now Wrapf is effectively obsolete.

    So this brings us to the present day recommendations:

    1. If you want stack traces in your errors, use github.com/pkg/errors.Wrap to wrap errors.
    2. If you don't care about stack traces, use fmt.Errorf from the standard library.
    3. Never use errors.Errorf since it breaks error unwrapping (e.g. errors.Is).
    4. If you want to minimize your usage of github.com/pkg/errors, you can use errors.WithStack(fmt.Errorf(...))

    *The github.com/pkg/errors package is no longer actively maintained. The repo has been archived, and is no longer accepting updates, but remains available for use.

    **The standard library's error package still does not include stack traces, so github.com/pkg/errors is still popular for that.