===== Error wrapping (%w) =====
==== What is it? ====
**Error wrapping** means adding context to an error while keeping the original error inside it.
In Go, `fmt.Errorf` supports wrapping using `%w`:
fmt.Errorf("doing X: %w", err)
==== What is it used for? ====
* Add helpful context at each layer (repo → usecase → handler).
* Preserve the root cause so callers can detect it with `errors.Is/As`.
==== Example ====
if err := dbCall(); err != nil {
return fmt.Errorf("update user: %w", err)
}
==== Notes / pitfalls ====
* Use `%w` only once in a single `fmt.Errorf` call.
* Use `%v` if you only want formatting without wrapping.
* Wrapping enables `errors.Is` and `errors.As` to work through layers.
===== Error wrapping (%w) vs formatting (%v) =====
==== What is it? ====
In `fmt.Errorf`, you can use:
* `%w` to **wrap** an error (keeps the original error inside)
* `%v` to **format** an error (string formatting only; does NOT wrap)
==== Why it matters ====
* `%w` enables `errors.Is` / `errors.As` to find the root cause through wrapped layers.
* `%v` only prints the error message; `errors.Is` will not match the original error.
==== Example code ====
package main
import (
"errors"
"fmt"
)
var ErrNotFound = errors.New("not found")
func repo() error {
return ErrNotFound
}
// Wrap with %w (keeps original error)
func usecaseWrap() error {
if err := repo(); err != nil {
return fmt.Errorf("usecase (wrap): %w", err)
}
return nil
}
// Format only with %v (does NOT wrap)
func usecaseFormatOnly() error {
if err := repo(); err != nil {
return fmt.Errorf("usecase (format only): %v", err)
}
return nil
}
func main() {
errW := usecaseWrap()
errV := usecaseFormatOnly()
fmt.Println("== %w ==")
fmt.Println("Error:", errW)
fmt.Println("errors.Is(errW, ErrNotFound):", errors.Is(errW, ErrNotFound))
fmt.Println("\n== %v ==")
fmt.Println("Error:", errV)
fmt.Println("errors.Is(errV, ErrNotFound):", errors.Is(errV, ErrNotFound))
}
==== Example output ====
== %w ==
Error: usecase (wrap): not found
errors.Is(errW, ErrNotFound): true
== %v ==
Error: usecase (format only): not found
errors.Is(errV, ErrNotFound): false
==== Rule of thumb ====
* Use `%w` when you want callers to detect the original error using `errors.Is/As`.
* Use `%v` when you only want a formatted message and do not need error matching.
==== Related pages ====
* [[go:errors:error_wrapping|Error wrapping (%w)]]
* [[go:errors:errors_is_as|errors.Is / errors.As]]
* [[go:errors:sentinel_errors|Sentinel errors]]
==== Hard words (English) ====
* **wrap** /ræp/: bọc
* **format** /ˈfɔːrmæt/: định dạng
* **root cause** /ruːt kɔːz/: nguyên nhân gốc
* **detect** /dɪˈtekt/: phát hiện/nhận biết
* **matching** /ˈmætʃɪŋ/: so khớp
* **preserve** /prɪˈzɝːv/: giữ lại
* **layer** /ˈleɪər/: tầng