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.