pkg / errors

Simple error handling primitives

Home Page:https://godoc.org/github.com/pkg/errors

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Proposal: Wrap(nil) should not return nil

adamzdara opened this issue · comments

I have recently run several times into issue when I or somebody else copied error handling code from some other case:

if err != nil {
  return errors.Wrap(err, "some message")
}

and used it for handling of some cases when err is nil and did not changed Wrap() function to New() eg:

// err was previously declared for some other error handling
if a <= 0 {
 return errors.Wrap(err, "a must be positive")
}

...and this is problem because in this case function will terminate with error but returns nil (because errors.Wrap(nil) returns nil). So my proposal here is to change this behaviour in this case to return errors.New(message) instead of nil.

I think the package is functioning as intended.

In the case above you should return errors.New() because the error was already handled. I think it's beyond the scope of this package to annotate an unhandled error.

@adamzdara consider this thread #90

While pkg/errors has not hit v1.0 there are a number of people already relying upon the behavior of this library, and as such, pkg/errors is considered to be covered under the same guidelines as the Go 1.0 consistency guidelines.

As the documentation clearly states that the intended behavior is that errors.Wrap will return nil if the err == nil, we cannot just change this behavior without breaking other people’s code.

It is not and cannot be the purpose of this package to help developers ensure that they have not copy-pasted inappropriate code from one line to another.

All makes sense. Just wanted to make sure that this is intentional. Will fork it for me. Thank you for info.

I also want to know why you want to return a new error when the function you are calling doesn't?

I also want to know why you want to return a new error when the function you are calling doesn't?

This is quite common in, for example, http.Client wrappers, where a return of err == nil in http.Client.Do means only that there was no error in the HTTP protocol. However, the http.Response may have a 4xx/5xx error in the StatusCode field which is not an HTTP protocol error, but rather an API-level error. In this case one then needs to use errors.New to return the API error to the caller of the wrapper.

But since one needs the http.Response from the http.Client.Do, one must have an err defined at the same scope. So, now one has an err defined, which will ensure that errors.Wrap(err, ...) will not throw a compile-time error. And now one has mistakenly created a bug.

ref: this was also discussed on #90.

My personal conclusion is that while I have a case in mind where I want a nil=>nil behavior, I don't want to spell this Wrapf(err, "Problem") because that's entirely unobvious to the reader.
The opposite is not obvious either — the "wrap" is just not a verb that has obvious meaning on nothing... If it was say "annotate", it would maybe lean towards nil=>nil.
So if I rely on nil=>nil, I'd want a very explicit name to draw attention, e.g. WrapIfError or something.

I'll only use Wrapf inside if err != nil blocks where there is not ambiguity.
And in complex cases, I guess I better do if { ... Wrapf... } else { ...New... }.

P.S. compare also to fmt.Errorf("...%w...", err) which always returns an error, matching %v behavior before %w was invented, and matching what you'd expect from "formatting".

As noted in #90, it is too late to change the behavior and semantics of this package.