How to compare Go errors
Declaring an error, and comparing it with '==
' (as in err == myPkg.ErrTokenExpired
) is no longer the best practice with Go 1.13 (Q3 2019)
The release notes mentions:
Go 1.13 contains support for error wrapping, as first proposed in the Error Values proposal and discussed on the associated issue.
An error
e
can wrap another errorw
by providing anUnwrap
method that returnsw
.
Bothe
andw
are available to programs, allowinge
to provide additional context tow
or to reinterpret it while still allowing programs to make decisions based onw
.To support wrapping,
fmt.Errorf
now has a%w
verb for creating wrapped errors, and three new functions in theerrors
package (errors.Unwrap
,errors.Is
anderrors.As
) simplify unwrapping and inspecting wrapped errors.
So the Error Value FAQ explains:
You need to be prepared that errors you get may be wrapped.
If you currently compare errors using
==
, useerrors.Is
instead.
Example:if err == io.ErrUnexpectedEOF
becomes
if errors.Is(err, io.ErrUnexpectedEOF)
- Checks of the form if
err != nil
need not be changed.- Comparisons to
io.EOF
need not be changed, becauseio.EOF
should never be wrapped.If you check for an error type using a type assertion or type switch, use
errors.As
instead. Example:if e, ok := err.(*os.PathError); ok
becomes
var e *os.PathError if errors.As(err, &e)
Also use this pattern to check whether an error implements an interface. (This is one of those rare cases when a pointer to an interface is appropriate.)
Rewrite a type switch as a sequence of
if-elses
.
This answer is for Go 1.12 and earlier releases.
Define an error value in a library
package fruits
var NoMorePumpkins = errors.New("No more pumpkins")
Do not create errors with errors.New
anywhere in the code but return the predefined value whenever error occurs and then you can do the following:
package shop
if err == fruits.NoMorePumpkins {
...
}
See io
package errors for reference.
This can be improved by adding methods to hide the check implementation and make the client code more immune to changes in fruits
package.
package fruits
func IsNoMorePumpkins(err error) bool {
return err == NoMorePumpkins
}
See os
package errors for reference.
Try
err.Error() == "Token is expired"
Or create your own error by implementing the error interface.