Day 018 — Errors as Values & Wrapping (%w)¶
Month 1 · Week 3 · ⬅ Day 017 · Day 019 ➡ · Journal index
🎯 Learning Objective¶
Treat errors as ordinary values, handle them explicitly, and build informative error chains by wrapping with fmt.Errorf("...: %w", err).
📚 Topics¶
erroris an interface · the(value, error)return idiom- Handle, don't ignore · don't
panicfor ordinary failures - Wrapping with
%wvs formatting with%v·errors.Unwrap - Adding context at each layer without leaking implementation
📖 Reading / Sources¶
- Learning Go (Bodner) ch.9 — Errors
- Go blog — Working with Errors in Go 1.13
- Go blog — Error handling and Go
- pkg
errors·fmt.Errorf
📝 Notes¶
erroris just an interface:type error interface{ Error() string }. Errors are values you compare, wrap, and pass around — not exceptions → [[errors-are-values]].- The idiom is the multi-return
(result, error). Check it immediately:if err != nil { return ..., err }. Never silently ignore an error with_unless you can articulate why. - Add context as you go up the stack so the final message reads like a path:
read config: open /etc/app.yaml: no such file or directory. Each layer prepends what it was doing. - Wrap with the
%wverb:fmt.Errorf("read config: %w", err). This stores the original error so callers can latererrors.Is/errors.As/errors.Unwrapit → [[error-wrapping]] (Day 019). - Use
%v(or%s) when you want to flatten the message and deliberately break the chain — e.g. to avoid leaking an internal sentinel across an API boundary. - A
%w-wrapped error implementsUnwrap() error;errors.Unwrap(err)peels one layer. You rarely call it directly —errors.Is/Aswalk the chain for you. - Don't double-decorate: avoid
failed to ...prefixes on every layer (the word "error/failed" repeats noisily). Keep context terse and lowercase, no trailing punctuation (Go style: error strings are not capitalized and don't end with punctuation). - For functions that take a
context.Context, it's the first parameter (ctx context.Context), and itsctx.Err()is itself an error you wrap/return → [[context-first-param]].
💻 Code Examples¶
func readConfig(path string) ([]byte, error) {
data, err := os.ReadFile(path)
if err != nil {
// %w keeps the original error in the chain for errors.Is/As later.
return nil, fmt.Errorf("read config %q: %w", path, err)
}
return data, nil
}
func main() {
_, err := readConfig("/no/such/file")
fmt.Println(err) // read config "/no/such/file": open ...: no such file or directory
fmt.Println(errors.Is(err, os.ErrNotExist)) // true — the wrapped os error is still reachable
}
Full code:
examples/month-01/errors/main.go· Run:go run ./examples/month-01/errors
🏋️ Exercises / Practice¶
| Exercise | Status | Link |
|---|---|---|
ParseConfig wrapping strconv/IO errors with %w |
✅ | exercises/month-01/week-3/validate |
Round-trip: wrap then errors.Is the original |
✅ | exercises/month-01/week-3/validate |
🐛 Mistakes Made¶
- Used
%vto wrap, thenerrors.Is(err, os.ErrNotExist)returned false because the chain was broken. Switched to%w. - Capitalized an error string (
"Failed to open") —go vet/style says error strings should be lowercase, no trailing period.
❓ Open Questions¶
- How much context is too much? (Each layer adds one short clause about its operation; don't repeat the layer below.)
🧠 Active Recall (answer without looking)¶
-
Q: What's the difference between wrapping with
%wand%vinfmt.Errorf?
A
`%w` preserves the original error in the chain (so `errors.Is`/`As`/`Unwrap` can find it); `%v` only formats the message text and breaks the chain. -
Q: What is the
errortype, fundamentally?
A
An interface with one method: `Error() string`. Errors are ordinary values returned alongside results, not thrown exceptions.
🪶 Feynman Reflection¶
In Go an error is just a value you carry back from a function, like a receipt that says "this went wrong, here's why." Wrapping with %w staples each layer's note onto the receipt without throwing away the layers underneath, so the caller can read the whole story — or programmatically find a specific note deep in the stack.
🕳️ Knowledge Gaps¶
- Deciding which boundaries should wrap vs flatten errors — clearer after Day 019's sentinel/custom errors.
✅ Summary¶
I handle errors as values returned from functions, check them immediately, and wrap with %w to build readable, machine-inspectable error chains.
⏭️ Next Steps / Prep for Tomorrow¶
- Day 019: sentinel and custom errors, plus
errors.Is,errors.As, anderrors.Join.
| Time spent | Difficulty | Confidence |
|---|---|---|
| 90 min | 🟦🟦⬜⬜⬜ | 🟦🟦🟦⬜⬜ |
Suggested commit: feat(examples): errors as values and %w wrapping (day 018)