Error Wrapping and Inspection
Unwrap Chain
Unwrapping one level at a time reveals the chain of context around an error.
unwrap
`errors.Unwrap` exposes the next error in the chain, which is useful when teaching how wrapping stores causes.
Unwrap Chain
unwrap_chain.go
Replay: real traced execution (multi-file project)
package main
import (
"errors"
"fmt"
)
func main() {
var step = "parse"
base := errors.New("invalid value")
first := fmt.Errorf("%s failed: %w", step, base)
second := fmt.Errorf("request failed: %w", first)
next := errors.Unwrap(second)
root := errors.Unwrap(next)
fmt.Println("step=", step)
fmt.Println("outer=", second.Error())
fmt.Println("next=", next.Error())
fmt.Println("root=", root.Error())
}
package main
import (
"errors"
"fmt"
)
func main() {
var step = "open"
base := errors.New("invalid value")
first := fmt.Errorf("%s failed: %w", step, base)
second := fmt.Errorf("request failed: %w", first)
next := errors.Unwrap(second)
root := errors.Unwrap(next)
fmt.Println("step=", step)
fmt.Println("outer=", second.Error())
fmt.Println("next=", next.Error())
fmt.Println("root=", root.Error())
}
package main
import (
"errors"
"fmt"
)
func main() {
var step = "decode"
base := errors.New("invalid value")
first := fmt.Errorf("%s failed: %w", step, base)
second := fmt.Errorf("request failed: %w", first)
next := errors.Unwrap(second)
root := errors.Unwrap(next)
fmt.Println("step=", step)
fmt.Println("outer=", second.Error())
fmt.Println("next=", next.Error())
fmt.Println("root=", root.Error())
}
step ← "parse", base ← &errors.errorString{s:"invalid value"}
8func main() {9 var step→ "parse" = "parse" //@step="open", "decode"10 base→ &errors.errorString{s:"invalid value"} := errors.New("invalid value")11 first→ &fmt.wrapError{msg:"parse failed: invalid value", err:(*errors.errorString)(⟨addr A⟩)} := fmt.Errorf("%s failed: %w", step"parse", base&errors.errorString{s:"invalid value"})12 second→ &fmt.wrapError{msg:"request failed: parse failed: invalid value", err:(*fmt.wrapError)(⟨addr B⟩)} := fmt.Errorf("request failed: %w", first&fmt.wrapError{msg:"parse failed: invalid value", err:(*errors.errorString)(⟨addr A⟩)})13 next→ &fmt.wrapError{msg:"parse failed: invalid value", err:(*errors.errorString)(⟨addr A⟩)} := errors.Unwrap(second&fmt.wrapError{msg:"request failed: parse failed: invalid value", err:(*fmt.wrapError)(⟨addr B⟩)})14 root→ &errors.errorString{s:"invalid value"} := errors.Unwrap(next&fmt.wrapError{msg:"parse failed: invalid value", err:(*errors.errorString)(⟨addr A⟩)})1516 fmt.Println("step=", step"parse")17 fmt.Println("outer=", second&fmt.wrapError{msg:"request failed: parse failed: invalid value", err:(*fmt.wrapError)(⟨addr B⟩)}.Error())18 fmt.Println("next=", next&fmt.wrapError{msg:"parse failed: invalid value", err:(*errors.errorString)(⟨addr A⟩)}.Error())19 fmt.Println("root=", root&errors.errorString{s:"invalid value"}.Error())20}outputstep= parse outer= request failed: parse failed: invalid value next= parse failed: invalid value root= invalid value
step ← "open", base ← &errors.errorString{s:"invalid value"}, first ← &fmt.wrapError{msg:"open failed: invalid value", err:(*errors.errorString)(⟨addr A⟩)}
8func main() {9 var step→ "open" = "open"10 base→ &errors.errorString{s:"invalid value"} := errors.New("invalid value")11 first→ &fmt.wrapError{msg:"open failed: invalid value", err:(*errors.errorString)(⟨addr A⟩)} := fmt.Errorf("%s failed: %w", step"open", base&errors.errorString{s:"invalid value"})12 second→ &fmt.wrapError{msg:"request failed: open failed: invalid value", err:(*fmt.wrapError)(⟨addr B⟩)} := fmt.Errorf("request failed: %w", first&fmt.wrapError{msg:"open failed: invalid value", err:(*errors.errorString)(⟨addr A⟩)})13 next→ &fmt.wrapError{msg:"open failed: invalid value", err:(*errors.errorString)(⟨addr A⟩)} := errors.Unwrap(second&fmt.wrapError{msg:"request failed: open failed: invalid value", err:(*fmt.wrapError)(⟨addr B⟩)})14 root→ &errors.errorString{s:"invalid value"} := errors.Unwrap(next&fmt.wrapError{msg:"open failed: invalid value", err:(*errors.errorString)(⟨addr A⟩)})1516 fmt.Println("step=", step"open")17 fmt.Println("outer=", second&fmt.wrapError{msg:"request failed: open failed: invalid value", err:(*fmt.wrapError)(⟨addr B⟩)}.Error())18 fmt.Println("next=", next&fmt.wrapError{msg:"open failed: invalid value", err:(*errors.errorString)(⟨addr A⟩)}.Error())19 fmt.Println("root=", root&errors.errorString{s:"invalid value"}.Error())20}outputstep= open outer= request failed: open failed: invalid value next= open failed: invalid value root= invalid value
step ← "decode", base ← &errors.errorString{s:"invalid value"}
8func main() {9 var step→ "decode" = "decode"10 base→ &errors.errorString{s:"invalid value"} := errors.New("invalid value")11 first→ &fmt.wrapError{msg:"decode failed: invalid value", err:(*errors.errorString)(⟨addr A⟩)} := fmt.Errorf("%s failed: %w", step"decode", base&errors.errorString{s:"invalid value"})12 second→ &fmt.wrapError{msg:"request failed: decode failed: invalid value", err:(*fmt.wrapError)(⟨addr B⟩)} := fmt.Errorf("request failed: %w", first&fmt.wrapError{msg:"decode failed: invalid value", err:(*errors.errorString)(⟨addr A⟩)})13 next→ &fmt.wrapError{msg:"decode failed: invalid value", err:(*errors.errorString)(⟨addr A⟩)} := errors.Unwrap(second&fmt.wrapError{msg:"request failed: decode failed: invalid value", err:(*fmt.wrapError)(⟨addr B⟩)})14 root→ &errors.errorString{s:"invalid value"} := errors.Unwrap(next&fmt.wrapError{msg:"decode failed: invalid value", err:(*errors.errorString)(⟨addr A⟩)})1516 fmt.Println("step=", step"decode")17 fmt.Println("outer=", second&fmt.wrapError{msg:"request failed: decode failed: invalid value", err:(*fmt.wrapError)(⟨addr B⟩)}.Error())18 fmt.Println("next=", next&fmt.wrapError{msg:"decode failed: invalid value", err:(*errors.errorString)(⟨addr A⟩)}.Error())19 fmt.Println("root=", root&errors.errorString{s:"invalid value"}.Error())20}outputstep= decode outer= request failed: decode failed: invalid value next= decode failed: invalid value root= invalid value