Wrapping Errors
Wrapping with %w
fmt.Errorf's %w verb, used in place of %v or %s, wraps the given error inside a new one, adding a message prefix while preserving the ability to unwrap back to the original.
Example: Wrapping with %w
package main
import (
"errors"
"fmt"
)
func readConfig() error {
return errors.New("file missing")
}
func startApp() error {
if err := readConfig(); err != nil {
return fmt.Errorf("startApp: %w", err)
}
return nil
}
func main() {
fmt.Println(startApp())
}
Login to try C/C++/Java/PHP code in the editor
Checking Through a Wrapped Chain
Even after several layers of wrapping, errors.Is can still detect the original error at the bottom of the chain, letting each layer add context without breaking error identity checks further up the call stack.
Example: Checking Through a Wrapped Chain
package main
import (
"errors"
"fmt"
)
var ErrConfigMissing = errors.New("config missing")
func readConfig() error {
return ErrConfigMissing
}
func startApp() error {
if err := readConfig(); err != nil {
return fmt.Errorf("startApp failed: %w", err)
}
return nil
}
func main() {
err := startApp()
fmt.Println(err)
fmt.Println("is config missing:", errors.Is(err, ErrConfigMissing))
}
Login to try C/C++/Java/PHP code in the editor
Manually Unwrapping
errors.Unwrap retrieves the single error wrapped one level down, which is the primitive that errors.Is and errors.As use internally to walk the whole chain step by step.
Note: Prefer errors.Is/errors.As in application code -- Unwrap is mostly useful for writing your own error-inspection tools.
Example: Manually Unwrapping
package main
import (
"errors"
"fmt"
)
func main() {
base := errors.New("disk full")
wrapped := fmt.Errorf("save failed: %w", base)
inner := errors.Unwrap(wrapped)
fmt.Println("outer:", wrapped)
fmt.Println("inner:", inner)
}
Login to try C/C++/Java/PHP code in the editor
- Using %v instead of %w in fmt.Errorf, which loses the ability for errors.Is/errors.As to see the original wrapped error.
- Wrapping the same error many times through several layers, creating an error message so long it becomes unreadable.
- Forgetting that wrapping doesn't change what errors.Is/errors.As see -- they still find the originally wrapped sentinel or type.
- fmt.Errorf's %w verb wraps an existing error while adding additional context.
- A wrapped error still satisfies errors.Is/errors.As checks against the original error.
- Wrapping preserves the full chain of context as an error travels up through several function calls.
- Errors.Unwrap can manually retrieve the next error in a wrap chain, though Is/As are usually preferred.
Chapter Quiz — Complete all 6 topics to unlock
0/6 topics done
Complete these topics first: