patterngoModerate
Error wrapping with fmt.Errorf %w and unwrapping with errors.Is / errors.As
Viewed 0 times
Go 1.13+
error wrappingfmt.Errorf %werrors.Iserrors.Aserror chainsentinel errorerror unwrap
Problem
Errors returned from deep call stacks lose context when returned bare. Before Go 1.13, adding context required third-party packages and broke sentinel error comparisons.
Solution
Use fmt.Errorf with %w to wrap errors, and errors.Is / errors.As to inspect them:
var ErrNotFound = errors.New("not found")
func fetchUser(id int) (*User, error) {
u, err := db.Query(id)
if err != nil {
return nil, fmt.Errorf("fetchUser %d: %w", id, err)
}
return u, nil
}
// Caller
u, err := fetchUser(42)
if errors.Is(err, ErrNotFound) {
// handles wrapped ErrNotFound anywhere in the chain
}
// Unwrap to concrete type
var dbErr *DBError
if errors.As(err, &dbErr) {
log.Println(dbErr.Code)
}Why
fmt.Errorf with %w stores the original error in the wrapper. errors.Is walks the entire chain calling Unwrap() at each step. errors.As does the same but checks assignability to a target type.
Gotchas
- Using %v instead of %w creates a string wrapper that cannot be unwrapped — errors.Is will fail
- errors.As requires a pointer to the target type (e.g. **MyError or *MyError depending on the receiver)
- Wrapping adds a stack of messages — trim them with a logger rather than printing err.Error() everywhere
Revisions (0)
No revisions yet.