The current implementation of restyError only unwraps to the inner error, which results in error unwrapping functions, such as errors.Is, only registering the first error that occurred.
This was a particularly confusing error to encounter, as the error is presented as the last, while unwrapping to the first.
Here is a minimal example of the issue:
playground
package main
import (
"errors"
"fmt"
)
func wrapErrors(n error, inner error) error {
if n == nil && inner == nil {
return nil
}
if inner == nil {
return n
}
if n == nil {
return inner
}
return &restyError{
err: n,
inner: inner,
}
}
type restyError struct {
err error
inner error
}
func (e *restyError) Error() string {
return e.err.Error()
}
func (e *restyError) Unwrap() error {
return e.inner
}
func main() {
errs := []error{
errors.New("Zero"),
errors.New("One"),
errors.New("Two"),
}
var cascade error
for _, err := range errs {
cascade = wrapErrors(err, cascade)
}
fmt.Println("Is", errs[0], errors.Is(cascade, errs[0]))
fmt.Println("Is", errs[1], errors.Is(cascade, errs[1]))
fmt.Println("Is", errs[2], errors.Is(cascade, errs[2]))
fmt.Println("String:", cascade)
// Output:
// Is Zero true
// Is One false
// Is Two false
// String: Two
}
This can be resolved by replacing the Unwrap() error implementation with Unwrap() []error like so:
func (e *restyError) Unwrap() []error {
return []error{e.err, e.inner}
}
// Output:
// Is Zero true
// Is One true
// Is Two true
// String: Two
The current implementation of
restyErroronly unwraps to the inner error, which results in error unwrapping functions, such aserrors.Is, only registering the first error that occurred.This was a particularly confusing error to encounter, as the error is presented as the last, while unwrapping to the first.
Here is a minimal example of the issue:
playground
This can be resolved by replacing the
Unwrap() errorimplementation withUnwrap() []errorlike so: