errors
errors copied to clipboard
Proposal: errors.Pop()
Hello, we are wrapping our errors in our grpc service, and we came across a situation where we wanted to return the top of the wrapped errors in the return of our endpoints.
For reference, a typical grpc endpoint signature looks like this:
func (s *Server) Hello(ctx context.Context, req *pb.HelloReq) (*pb.HelloResp, error) {
// ...
}
The method signature has an error type as the second return value. We've written grpc middleware that receives the return values of this function, and does the proper logging, metrics, etc, before ultimately returning to the requesting client.
In the middleware, we log the entire error chain, but what we'd like to return to the grpc client in the error return param is the top error in the chain. So I'm proposing the ability to do this:
first := errors.New("first")
second := errors.Wrap(first, "second")
third := errors.Wrap(second, "third")
// prints the entire chain:
third.Error() // [ third: second: first ]
// proposed option:
errors.Pop(third) // returns an error type with ONLY "third" and no chain attached
Currently there's no easy (that I know of 😄 ) way to get the top most of an error chain. This would allow us to do so.
Thoughts?
Thank you for this suggestion. I understand what you want to implement, but I do not understand why. Can you show me how a caller of Pop would use the result?
Hi!
I'm also agree with @bobbytables.
In my case a Pop() function is useful to isolate my library errors form others library.
Sometimes I wrap errors inside my function (which returns error), other times no.
var (
ErrNetwork = errors.New("network erorr")
ErrDB = errors.New("database erorr")
)
func main() {
err := funcWithError()
switch errors.Pop(err) {
case ErrNetwork:
// do stuff
case ErrDB:
// do stuff
default:
// do other stuff
}
...
}
@tux-eithel thanks for your comment. I don't think your sample code works because the errors returned from this package cannot be compared for equality. This is by design.
@davecheney My example it too much simplified. The Pop() may returns an errors and the switch could be done on after call Error() function.
var (
ErrNetwork = errors.New("network erorr")
ErrDB = errors.New("database erorr")
)
func main() {
err := funcWithError()
pop := errors.Pop(err)
switch pop.Error() {
case ErrNetwork:
// do stuff
case ErrDB:
// do stuff
default:
// some error from other libraries
}
...
}
You can write errors.Pop yourself
if err, ok := err.(interface{Cause() error}); ok {
cause := err.Cause()
///
}