errwrap
is a package for Go that formalizes the pattern of wrapping errors
and checking if an error contains another error.
There is a common pattern in Go of taking a returned error
value and
then wrapping it (such as with fmt.Errorf
) before returning it. The problem
with this pattern is that you completely lose the original error
structure.
Arguably the correct approach is that you should make a custom structure
implementing the error
interface, and have the original error as a field
on that structure, such as this example.
This is a good approach, but you have to know the entire chain of possible
rewrapping that happens, when you might just care about one.
errwrap
formalizes this pattern (it doesn't matter what approach you use
above) by giving a single interface for wrapping errors, checking if a specific
error is wrapped, and extracting that error.
Install using go get github.com/hashicorp/errwrap
.
Full documentation is available at http://godoc.org/github.com/hashicorp/errwrap
Below is a very basic example of its usage:
// A function that always returns an error, but wraps it, like a real
// function might.
func tryOpen() error {
_, err := os.Open("/i/dont/exist")
if err != nil {
return errwrap.Wrapf("Doesn't exist: {{err}}", err)
}
return nil
}
func main() {
err := tryOpen()
// We can use the Contains helpers to check if an error contains
// another error. It is safe to do this with a nil error, or with
// an error that doesn't even use the errwrap package.
if errwrap.Contains(err, "does not exist") {
// Do something
}
if errwrap.ContainsType(err, new(os.PathError)) {
// Do something
}
// Or we can use the associated `Get` functions to just extract
// a specific error. This would return nil if that specific error doesn't
// exist.
perr := errwrap.GetType(err, new(os.PathError))
}
If you're already making custom types that properly wrap errors, then
you can get all the functionality of errwraps.Contains
and such by
implementing the Wrapper
interface with just one function. Example:
type AppError {
Code ErrorCode
Err error
}
func (e *AppError) WrappedErrors() []error {
return []error{e.Err}
}
Now this works:
err := &AppError{Err: fmt.Errorf("an error")}
if errwrap.ContainsType(err, fmt.Errorf("")) {
// This will work!
}