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

errwrap (incorrectly?) identifies missing error-wrapping directive %w #21

Open
atc0005 opened this issue Mar 28, 2023 · 2 comments
Open

Comments

@atc0005
Copy link
Contributor

atc0005 commented Mar 28, 2023

Overview

Hi @fatih,

Thank you for providing this linter. It has already helped catch several cases where I did not use the %w error-wrapping directive in my projects.

It did however flag one case that may be a false-positive.

I have a project that is still using Go 1.19 and the linter identifies what it believes to be a missing use of %w, but I already have one such usage in place. I understand that this syntax would be valid in Go 1.20, but since the project README indicates that the linter supports Go 1.16 and newer I'm reporting this as a potential problem.

Short version (before following linter advice):

annotatedErrors := make([]error, 0, len(errs))

// ...
annotatedErrors = append(annotatedErrors, fmt.Errorf(
    "%w: %v; %s",
    err,
    errRuntimeTimeoutReached,
    runtimeTimeoutReachedAdvice,
))

Linter output:

$ errwrap main.go
main.go:41:48: call could wrap the error with error-wrapping directive %w

Reproducible example

Code after following linter advice:

code example
// main.go
package main

import (
	"context"
	"errors"
	"fmt"
)

// errRuntimeTimeoutReached indicates that plugin runtime exceeded specified
// timeout value.
var errRuntimeTimeoutReached = errors.New("plugin runtime exceeded specified timeout value")

// runtimeTimeoutReachedAdvice offers advice to the sysadmin for routine
// occurrence.
const runtimeTimeoutReachedAdvice string = "consider increasing value if this is routinely encountered"

func annotateError(errs ...error) []error {

	isNilErrCollection := func(collection []error) bool {
		if len(collection) != 0 {
			for _, err := range errs {
				if err != nil {
					return false
				}
			}
		}
		return true
	}

	switch {

	// Process errors as long as the collection is not empty or not composed
	// entirely of nil values.
	case !isNilErrCollection(errs):
		annotatedErrors := make([]error, 0, len(errs))

		for _, err := range errs {
			if err != nil {
				switch {
				case errors.Is(err, context.DeadlineExceeded):
					annotatedErrors = append(annotatedErrors, fmt.Errorf(
						"%w: %w; %s",
						err,
						errRuntimeTimeoutReached,
						runtimeTimeoutReachedAdvice,
					))

				default:
					// Record error unmodified if additional decoration isn't defined
					// for the error type.
					annotatedErrors = append(annotatedErrors, err)
				}
			}
		}

		return annotatedErrors

	// No errors were provided for evaluation.
	default:
		return nil
	}

}

func main() {
	sampleErr := fmt.Errorf(
		"my sample error: %w",
		context.DeadlineExceeded,
	)

	errCollection := annotateError(sampleErr)

	fmt.Println(errCollection)
}

// main_test.go
package main

import (
	"context"
	"fmt"
	"testing"
)

func TestAnnotateError(t *testing.T) {
	sampleErr := fmt.Errorf(
		"my sample error: %w",
		context.DeadlineExceeded,
	)

	errCollection := annotateError(sampleErr)

	t.Log(errCollection)
}

Results

If I follow the linter's advice, this is what I get when running the code:

$ go run main.go
[my sample error: context deadline exceeded: %!w(*errors.errorString=&{plugin runtime exceeded specified timeout value}); consider increasing value if this is routinely encountered]

$ go test ./...
# errwrap-false-positive
.\main.go:41:48: fmt.Errorf call has more than one error-wrapping directive %w
FAIL    errwrap-false-positive [build failed]
FAIL

Linter version details

$ go version -m $(which errwrap.exe)
C:/Users/atc0005/go/bin/errwrap.exe: go1.19.7
        path    github.com/fatih/errwrap
        mod     github.com/fatih/errwrap        v1.5.0  h1:/z6jzrekbYYeJukzq9h3nY+SHREDevEB0vJYC4kE9D0=
        dep     golang.org/x/mod        v0.8.0  h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8=
        dep     golang.org/x/sys        v0.5.0  h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
        dep     golang.org/x/tools      v0.6.0  h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM=
        build   -compiler=gc
        build   CGO_ENABLED=1
        build   CGO_CFLAGS=
        build   CGO_CPPFLAGS=
        build   CGO_CXXFLAGS=
        build   CGO_LDFLAGS=
        build   GOARCH=amd64
        build   GOOS=windows
        build   GOAMD64=v1
@nasreen-begum
Copy link

Yes, Even I am also facing same issue

@nasreen-begum
Copy link

cmd/telegraf/main.go:199: fmt.Errorf call has more than one error-wrapping directive %w

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