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

code
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)
}
  1. code ← "denied", err ← <nil>

    11func main() {12  var code→ "denied" = "denied" //@code="closed", "ok"13  var err→ <nil> error14  if code == "denied" {
  2. 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" {
  3. 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
  1. code ← "closed", err ← <nil>

    11func main() {12  var code→ "closed" = "closed"13  var err→ <nil> error14  if code == "denied" {
  2. 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}
  3. 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
  1. 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