Wrap & Unwrap errors in Go
- tags: Go
- source: “Wrap and Unwrap Errors in Go (Golang) | Gosamples.Dev.” Accessed October 12, 2022. https://gosamples.dev/wrap-unwrap-errors/.
Overview⌗
Since Go1.13, there is a new feature about error add to go: Wrap & Unwrap errors. Let’s start from a simple example:
package main
import (
"errors"
"fmt"
)
var errRollingInTheDeep = errors.New("rolling in the deep")
func doSomeActualJob() error {
return errRollingInTheDeep
}
func someFrameworkCaller() error {
// We use fmt.Errorf to wrap error.
// Notice the "%w" formater here and content surround the "[...]".
return fmt.Errorf("[someFrameworkCaller] %w", doSomeActualJob())
}
func someEntrance() error {
return fmt.Errorf("[someUpperCaller] %w", someFrameworkCaller())
}
func main() {
err := someEntrance()
if errors.Is(err, errRollingInTheDeep) {
fmt.Println("This should be")
} else {
panic("Oooops!")
}
fmt.Printf("The final error: %v\n", err)
fmt.Printf("Unwrap: %v\n", errors.Unwrap(err))
}
Its output:
This should be
The final error: [someUpperCaller] [someFrameworkCaller] rolling in the deep
Unwrap: [someFrameworkCaller] rolling in the deep
Let’s add more line to see how about to continual unwrap it:
package main
import (
"errors"
"fmt"
)
var errRollingInTheDeep = errors.New("rolling in the deep")
func doSomeActualJob() error {
return errRollingInTheDeep
}
func someFrameworkCaller() error {
// We use fmt.Errorf to wrap error.
// Notice the "%w" formater here and content surround the "[...]".
return fmt.Errorf("[someFrameworkCaller] %w", doSomeActualJob())
}
func someEntrance() error {
return fmt.Errorf("[someUpperCaller] %w", someFrameworkCaller())
}
func main() {
err := someEntrance()
if errors.Is(err, errRollingInTheDeep) {
fmt.Println("This should be")
} else {
panic("Oooops!")
}
fmt.Printf("The final error: %v\n", err)
err = errors.Unwrap(err)
if errors.Is(err, errRollingInTheDeep) {
fmt.Println("This should be either")
} else {
panic("Oooops!")
}
fmt.Printf("Unwrapped error: %v\n", errors.Unwrap(err))
err = errors.Unwrap(err)
fmt.Printf("Unwrapped error: %v\n", errors.Unwrap(err))
err = errors.Unwrap(err)
fmt.Printf("Still unwrap to see what we have got: %v\n", errors.Unwrap(err))
err = errors.Unwrap(err)
fmt.Printf("Still unwrap to see what we have got: %v\n", errors.Unwrap(err))
}
Its output:
This should be
The final error: [someUpperCaller] [someFrameworkCaller] rolling in the deep
This should be either
Unwrapped error: rolling in the deep
Unwrapped error: <nil>
Still unwrap to see what we have got: <nil>
Still unwrap to see what we have got: <nil>
As we can see above, here is the conclusions:
- The
errors.Is
could always check which the error is; - The
errors.Unwrap
returns error the first wrapped error directly, which means we can’t get the errors amid the chain; - Calling
errors.Unwrap
on a non-wrapped error or nil pointer, the nil pointer will always be returned.
Does it Help for Error Tracing Purpose?⌗
As I already known, some logging frameworks/utilities provide like https://github.com/uber-go/zap provide a mechanic to print the invocation stacktrace of error. I’m wonder if this help.
Let’s see a example:
package main
import (
"errors"
"fmt"
"go.uber.org/zap"
)
var errRollingInTheDeep = errors.New("rolling in the deep")
func doSomeActualJob() error {
return errRollingInTheDeep
}
func someFrameworkCaller() error {
// We use fmt.Errorf to wrap error.
// Notice the "%w" formater here and content surround the "[...]".
return fmt.Errorf("[someFrameworkCaller] %w", doSomeActualJob())
}
func someEntrance() error {
return fmt.Errorf("[someUpperCaller] %w", someFrameworkCaller())
}
func main() {
logger, _ := zap.NewDevelopment()
defer logger.Sync() // flushes buffer, if any
err := someEntrance()
logger.Warn("ERROR", zap.Error(err))
}
Its output:
2022-10-12T10:52:32.792+0800 WARN demo/main.go:30 ERROR {"error": "[someUpperCaller] [someFrameworkCaller] rolling in the deep"}
main.main
/Users/wanghui/codes/notes/demo/main.go:30
runtime.main
/opt
Ooops, the zap output output stacktrace based where its call, not the error.
We can do it with a Stack field that provided zap.
Conclusion: it does help for error tracing purpose, but only with the provided context that surround by “[…]”.