%w Vs %v In Error Messages —
Fast recall
Quick revision
Core answer
Practical understanding of Fmt Errorf.
Repeating a definition without saying when to use it.
Give one trade-off and say how you would test it.
Interview-ready guide
Interview answer
The two lines look the same and behave differently:
base := errors.New("disk full")
withW := fmt.Errorf("write config: %w", base)
withV := fmt.Errorf("write config: %v", base)
// both print: "write config: disk full"
errors.Is(withW, base) // true
errors.Is(withV, base) // false — the chain is gone%w stores the cause as a linkable edge; %v renders the cause's message into the string at construction and keeps no reference. The information content of the message is identical — the difference is entirely whether the machine can still ask questions about the cause.
So the rule: %w whenever a caller below you might need to match or extract the cause (which is most wrapping — you rarely know your caller's needs). %v when the error is headed for a human log line and no code will inspect it — a top-level 'failed to start: %v' that nothing branches on.
The bug shape to recognize: someone 'cleaned up' %w to %v for style, and six months later a caller's errors.Is quietly became always-false. The test that would catch it is a test on the wrap — which most codebases never write.
%w Vs %v In Error Messages —
Direct Answer
They print identically. %w preserves the unwrap chain (errors.Is/As work); %v bakes the text in and cuts the chain.
Deep Dive & Architecture
The printed output is byte-identical; the internal structure is completely different. It's worth seeing once what %v actually does to the cause:
%v calls the cause's Error() method AT FORMAT TIME and interpolates the string. The resulting wrapError holds your formatted text — the cause's message is now embedded characters, and the cause value itself was discarded after formatting. The chain ends: Unwrap() on the result has nothing to return.
%w stores the cause as a field. The message is built the same way (the cause's text appears identically), but the wrapError also implements Unwrap() -> cause. The errors package's Is/As walk that edge.
So the mental model: both verbs render the cause's MESSAGE; only %w preserves the cause's IDENTITY. Text vs identity is the whole difference — and every downstream question (Is this a DiskFull? What's the RateLimit's duration?) is an identity question. Answers that require identity need %w; answers satisfied by prose don't.
The migration trap: codebases that started %v-everywhere (or pre-1.13, before %w existed) have wraps that LOOK chainable but aren't. errors.Is through them is silently false. The audit is mechanical — grep for Errorf calls with %v and an err argument, and check each: terminal-format (fine) or chain-middle (should be %w).
Common Mistakes & Pitfalls
Repeating a definition without saying when to use it.
Check your understanding
Answer these without looking back at the article.
- What is Fmt Errorf, and when would you use it?
- How would you compare Fmt Errorf with an alternative?
- What is a common mistake when using Fmt Errorf?
Looking for a structured roadmap?
Every module on this page is also part of the full Java backend interview track.