Go

What is the difference between %w and %v in fmt.Errorf?

Question 296MediumGo 1.22 to 1.25

Both produce the same message text. %w also wraps: the returned error has an Unwrap() error method (or Unwrap() []error when there are several %w verbs, Go 1.20+), so errors.Is and errors.As can see the original. %v flattens the cause into a string and cuts the chain.

var ErrNotFound = errors.New("not found")

e1 := fmt.Errorf("get user 42: %w", ErrNotFound)
e2 := fmt.Errorf("get user 42: %v", ErrNotFound)

fmt.Println(errors.Is(e1, ErrNotFound)) // true
fmt.Println(errors.Is(e2, ErrNotFound)) // false
fmt.Println(e1.Error() == e2.Error())   // true

e3 := fmt.Errorf("a: %w, b: %w", io.EOF, fs.ErrClosed) // Go 1.20+
fmt.Println(errors.Is(e3, fs.ErrClosed))                // true

Gotchas: %w works only in fmt.Errorf. Its operand must implement error, otherwise you get %!w(...) in the text (and go vet flags it). Choosing %w is an API decision: callers can now depend on the wrapped error. Use %v when the cause is an implementation detail you don't want to expose.

More on Error Handling & panics

All 37 Error Handling & panics questions