fleet icon indicating copy to clipboard operation
fleet copied to clipboard

"unwrap" pkg/errors errors in the transport encoder

Open groob opened this issue 7 years ago • 0 comments

We use the errors.Wrap(err, "additional info about the error") patter to wrap increasingly more errors in the app. This is great, but the error is wrapped into a new type to add context. Inside the encodeError function in transport_error.go we might need to "unwrap" the error in at the top of the errorEncoder function to determine it's type in order to properly encode it to the client.

example:

package main

import (
	"testing"

	"github.com/pkg/errors"
)

package main

import (
	"fmt"
	"testing"

	"github.com/pkg/errors"
)

func TestUnwrap(t *testing.T) {
	// passes
	if err := base(); err != baseError {
		t.Errorf("expected base error, got %s", err)
	}

	// fails:
	// 	main_test.go:16: expected base error, got additional context: an error
	if err := wrapped(baseError); err != baseError {
		t.Errorf("expected base error, got %s", err)
	}

	// passes
	if err := wrapped(baseError); errors.Cause(err) != baseError {
		t.Errorf("expected base error, got %s", err)
	}

	// Or with wrapped typed errors
	type Retriable interface {
		error
		Temporary() bool
	}

	var rErr error
	rErr = &e{}
	rErr = wrapped(rErr) // wrap here

	if e, ok := rErr.(Retriable); ok {
		// will never print because it's wrapped
		fmt.Println(e.Error())
	}
}

func wrapped(err error) error {
	return errors.Wrap(err, "additional context")
}

type e struct{}

func (err *e) Error() string {
	return "an error"
}

func (err *e) Temporary() bool {
	return true
}

var baseError = errors.New("an error")

func base() error {
	return baseError
}

groob avatar Mar 14 '17 20:03 groob