What does fmt.Errorf("op: %w", err) return when err is nil? Why does this matter in helper functions?
Question 319HardGo 1.22 to 1.25
It returns a non-nil error whose message is op: %!w(<nil>). fmt.Errorf always returns a new error value, and it doesn't care whether the operand is nil. So an unconditional "wrap" helper turns success into failure.
func wrap(op string, err error) error {
return fmt.Errorf("%s: %w", op, err) // BUG when err == nil
}
func wrapSafe(op string, err error) error {
if err == nil {
return nil
}
return fmt.Errorf("%s: %w", op, err)
}
func main() {
fmt.Println(wrap("save", nil) == nil) // false
fmt.Println(wrap("save", nil)) // save: %!w(<nil>)
fmt.Println(wrapSafe("save", nil) == nil) // true
}
A common pattern is to decorate every error a function returns, using a defer:
func Save(u User) (err error) {
defer func() {
if err != nil {
err = fmt.Errorf("save user %d: %w", u.ID, err)
}
}()
// ...
return nil
}
This pattern depends on named results. The err != nil guard is what keeps the nil case correct.
More on Error Handling & panics
- Q317Why is
defer f.Close()potentially a bug when writing files? How do you handle theCloseerror? - Q318What does this print? (Shadowed
err) - Q320Explain context errors:
context.Canceled,DeadlineExceeded, andcontext.Cause. - Q321Why is
errors.Is(err, fs.ErrNotExist)preferred overos.IsNotExist(err)? - Q322What is "asserting errors for behavior"? How would you implement retry logic based on error behavior?
- Q323How does
net/httphandle a panic in a handler? What are the pitfalls?