Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ErrorNode should not always implement Causer interface #13

Open
jjcollinge opened this issue Oct 24, 2018 · 1 comment
Open

ErrorNode should not always implement Causer interface #13

jjcollinge opened this issue Oct 24, 2018 · 1 comment

Comments

@jjcollinge
Copy link
Contributor

jjcollinge commented Oct 24, 2018

I am using the package github.com/Azure/azure-storage-blob-go/2016-05-31/azblob to add support for Azure Storage to the Pulumi project (pulumi/pulumi#2026). Currently, when I receive a StorageError the Pulumi codebase uses errors.Wrap(err, "") to add annotations to the original error. The error is then unwrapped using errors.Cause(err) to determine if the original error was a "not found" error (BlobNotFound in our case). However, as the current implementation of ErrorNode can always be type asserted against the Causer interface, even if the cause error is nil, it breaks the expected behavior for errors.Cause(err) defined here. I've inlined the code so that I can annotate.

func Cause(err error) error {
	type causer interface {
		Cause() error
	}

	for err != nil {
		cause, ok := err.(causer) //  1. This will return OK, even if the cause is nil
		if !ok {
			break // 4. We actually want to break here with the original error
		}
		err = cause.Cause() // 2. This will then return nil
	}
	return err // 3. Meaning this will return nil
}

This means we get nil back rather than the nested StorageError as expected and in accordance to how the standard library errors work.

I have created a temporary work around in pulumi by defining a new error struct that implements the StorageError interface but does not implement Causer and this has fixed my code. This is available here: https://github.com/pulumi/pulumi/blob/03d8939eee39599dcee8e3328fcf6c1da6ce1f47/pkg/backend/filestate/storage/azure/error.go

My proposal would be to define a new error type pcErrorNoCause and a new type ErrorNodeNoCause that doesn't implement the Causer interface. Then in NewError() error check whether the cause parameter is nil and if so return a pcErrorNoCause error instead of the existing pcError. Something similar to below:

// NewError creates a simple string error (like Error.New). But, this
// error also captures the caller's Program Counter and the preceding error.
func NewError(cause error, msg string) error {
        if cause != nil {
                return &pcError{
		    ErrorNode: ErrorNode{}.Initialize(cause, 3),
		    msg:       msg,
	        }
        }
	return &pcErrorNoCause{
		ErrorNode: ErrorNodeNoCause{}.Initialize(3),
		msg:       msg,
	}
}

Offending code:

func NewError(cause error, msg string) error {

Does this sound reasonable? If so, I am happy to PR the change.

@JeffreyRichter
Copy link
Member

JeffreyRichter commented Oct 24, 2018 via email

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants