Wrapping errors in Go means adding extra context information to the returned error like the name of the function where the error occurred, the cause, the type, etc. This technique is most commonly used to create clear error messages, which are especially useful for debugging when you want quickly and precisely locate the source of problems.
To wrap an error in Go, you need to create a new error using thefmt.Errorf(format string, a \.\.\.interface{}) error
function with the verb %w
in the format
:
var ErrorCritical = errors.New("critical error")
...
wrapped := fmt.Errorf("[functionName] internal error: %w", ErrorCritical)
The resulting error is a chain, in which the wrapped error can be ‘unwrapped’ using the errors.Unwrap()
function:
fmt.Println(errors.Unwrap(wrapped) == ErrorCritical) // true
It is also possible to check if a given error exists anywhere in the chain thanks to the errors.Is()
and errors.As()
functions. See the examples below for details on how to wrap, unwrap, and test for error types.
Check out more examples of error handling in Go using the
errors.Is()
anderrors.As()
functions in our other tutorial.
Examples
In the first example, we create the getError()
function that returns a non-wrap, single-wrap, or double-wrap error depending on the parameter set. The error we wrap is a simple built-in error
instance.
|
|
Output:
is error internal: [getData] level 1 error: internal error
unwrapped error: internal error
---
is error internal: [getData] level 2 error: [getData] level 1 error: internal error
unwrapped error: [getData] level 1 error: internal error
unwrapped unwrapped error: internal error
Let’s go through the main()
function and the output. In lines 25-29
, we get a single-wrap error and test if it is an ErrorInternal
error. As you can see, the errors.Is()
function returns true
because it checks if any error in the chain matches the target. It does not matter that the error is wrapped. A simple comparison if err == ErrorInternal
would give false
in this case, so it is generally a better idea to use the errors.Is()
function to compare errors equality. Then, we unwrap the error using the errors.Unwrap()
and print it to the standard output. Unwrapping the error gives the ErrorInternal
that we wrapped before.
In lines 33-39
, we get a double-wrap error. The errors.Is()
returns that the ErrorInternal
is in the chain, even though it is double-wrapped. As you might expect, a double unwrapping is needed to get to the ErrorInternal
.
Similarly, you can wrap, unwrap and test for errors of a specific type. Look at the second example below. The result is analogous to the first example with a simple error
instance. The only difference is the use of the errors.As()
function instead of errors.Is()
, which checks if the error in the chain is of the specific type.
More examples of error handling using the
errors.Is()
anderrors.As()
can be found here.
|
|
Output:
is error internal: level 1 error: [getData] error internal
unwrapped error: [getData] error internal
---
is error internal: level 2 error: level 1 error: [getData] error internal
unwrapped error: level 1 error: [getData] error internal
unwrapped unwrapped error: [getData] error internal