Skip to content

restyError only unwraps to innermost error #1143

@lukepmccombs

Description

@lukepmccombs

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions