Error Wrapping and Inspection
Sentinel Errors
A sentinel error gives a package a stable value that callers can compare through wrapping.
sentinel
Sentinel errors should be few, named, and checked through `errors.Is` so context can still be added.
Sentinel Errors
sentinel_errors.go
Replay: real traced execution (multi-file project)
package main
import (
"errors"
"fmt"
)
var ErrDenied = errors.New("permission denied")
var ErrClosed = errors.New("service closed")
func main() {
var code = "denied"
var err error
if code == "denied" {
err = fmt.Errorf("authorize: %w", ErrDenied)
} else if code == "closed" {
err = fmt.Errorf("connect: %w", ErrClosed)
}
denied := errors.Is(err, ErrDenied)
closed := errors.Is(err, ErrClosed)
fmt.Println("code=", code)
fmt.Println("denied=", denied)
fmt.Println("closed=", closed)
fmt.Println("has_error=", err != nil)
}
package main
import (
"errors"
"fmt"
)
var ErrDenied = errors.New("permission denied")
var ErrClosed = errors.New("service closed")
func main() {
var code = "closed"
var err error
if code == "denied" {
err = fmt.Errorf("authorize: %w", ErrDenied)
} else if code == "closed" {
err = fmt.Errorf("connect: %w", ErrClosed)
}
denied := errors.Is(err, ErrDenied)
closed := errors.Is(err, ErrClosed)
fmt.Println("code=", code)
fmt.Println("denied=", denied)
fmt.Println("closed=", closed)
fmt.Println("has_error=", err != nil)
}
package main
import (
"errors"
"fmt"
)
var ErrDenied = errors.New("permission denied")
var ErrClosed = errors.New("service closed")
func main() {
var code = "ok"
var err error
if code == "denied" {
err = fmt.Errorf("authorize: %w", ErrDenied)
} else if code == "closed" {
err = fmt.Errorf("connect: %w", ErrClosed)
}
denied := errors.Is(err, ErrDenied)
closed := errors.Is(err, ErrClosed)
fmt.Println("code=", code)
fmt.Println("denied=", denied)
fmt.Println("closed=", closed)
fmt.Println("has_error=", err != nil)
}
code ← "denied", err ← <nil>
11func main() {12 var code→ "denied" = "denied" //@code="closed", "ok"13 var err→ <nil> error14 if code == "denied" {err ← &fmt.wrapError{msg:"authorize: permission denied", err:(*errors.errorString)(⟨addr A⟩)}
13var err error14if code"denied" == "denied" {15 err→ &fmt.wrapError{msg:"authorize: permission denied", err:(*errors.errorString)(⟨addr A⟩)} = fmt.Errorf("authorize: %w", ErrDenied&errors.errorString{s:"permission denied"})16} else if code == "closed" {denied ← true, closed ← false
18 }19 denied→ true := errors.Is(err&fmt.wrapError{msg:"authorize: permission denied", err:(*errors.errorString)(⟨addr A⟩)}, ErrDenied&errors.errorString{s:"permission denied"})20 closed→ false := errors.Is(err&fmt.wrapError{msg:"authorize: permission denied", err:(*errors.errorString)(⟨addr A⟩)}, ErrClosed&errors.errorString{s:"service closed"})2122 fmt.Println("code=", code"denied")23 fmt.Println("denied=", deniedtrue)24 fmt.Println("closed=", closedfalse)25 fmt.Println("has_error=", err&fmt.wrapError{msg:"authorize: permission denied", err:(*errors.errorString)(⟨addr A⟩)} != nil)26}outputcode= denied denied= true closed= false has_error= true
code ← "closed", err ← <nil>
11func main() {12 var code→ "closed" = "closed"13 var err→ <nil> error14 if code == "denied" {err ← &fmt.wrapError{msg:"connect: service closed", err:(*errors.errorString)(⟨addr A⟩)}
15 err = fmt.Errorf("authorize: %w", ErrDenied)16} else if code"closed" == "closed" {17 err→ &fmt.wrapError{msg:"connect: service closed", err:(*errors.errorString)(⟨addr A⟩)} = fmt.Errorf("connect: %w", ErrClosed&errors.errorString{s:"service closed"})18}denied ← false, closed ← true
18 }19 denied→ false := errors.Is(err&fmt.wrapError{msg:"connect: service closed", err:(*errors.errorString)(⟨addr A⟩)}, ErrDenied&errors.errorString{s:"permission denied"})20 closed→ true := errors.Is(err&fmt.wrapError{msg:"connect: service closed", err:(*errors.errorString)(⟨addr A⟩)}, ErrClosed&errors.errorString{s:"service closed"})2122 fmt.Println("code=", code"closed")23 fmt.Println("denied=", deniedfalse)24 fmt.Println("closed=", closedtrue)25 fmt.Println("has_error=", err&fmt.wrapError{msg:"connect: service closed", err:(*errors.errorString)(⟨addr A⟩)} != nil)26}outputcode= closed denied= false closed= true has_error= true
code ← "ok", err ← <nil>, denied ← false, closed ← false
11func main() {12 var code→ "ok" = "ok"13 var err→ <nil> error14 if code == "denied" {15 err = fmt.Errorf("authorize: %w", ErrDenied)16 } else if code == "closed" {17 err = fmt.Errorf("connect: %w", ErrClosed)18 }19 denied→ false := errors.Is(err<nil>, ErrDenied&errors.errorString{s:"permission denied"})20 closed→ false := errors.Is(err<nil>, ErrClosed&errors.errorString{s:"service closed"})2122 fmt.Println("code=", code"ok")23 fmt.Println("denied=", deniedfalse)24 fmt.Println("closed=", closedfalse)25 fmt.Println("has_error=", err<nil> != nil)26}outputcode= ok denied= false closed= false has_error= false