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

step
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())
}
  1. 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
  1. 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
  1. 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