← Back to Go Course | Chapter 9: Error Handling | Lesson 5 of 6

Sentinel Errors

A sentinel error is a pre-made, specially named error value that acts like a known signpost -- callers can check for that exact signpost by name instead of reading the error's message text.

Defining a Sentinel Error

A sentinel error is simply a package-level variable created once with errors.New, meant to be compared against by identity rather than by message text -- it acts as a well-known signal for one specific failure condition.

Note: Name sentinel errors with an Err prefix, like ErrNotFound, by convention.

Example: Defining a Sentinel Error

markup
package main

import (
	"errors"
	"fmt"
)

var ErrNotFound = errors.New("resource not found")

func find(id int) error {
	if id != 1 {
		return ErrNotFound
	}
	return nil
}

func main() {
	err := find(2)
	fmt.Println(err)
}

Checking Against a Sentinel

Calling code checks for a sentinel error with errors.Is, comparing the returned error against the known sentinel value -- this correctly matches even if the error was wrapped along the way.

Example: Checking Against a Sentinel

markup
package main

import (
	"errors"
	"fmt"
)

var ErrNotFound = errors.New("resource not found")

func find(id int) error {
	if id != 1 {
		return ErrNotFound
	}
	return nil
}

func main() {
	err := find(5)
	if errors.Is(err, ErrNotFound) {
		fmt.Println("handle missing resource specifically")
	}
}

Sentinel Errors in the Standard Library

Go's standard library relies on this exact pattern -- io.EOF signals the end of a stream, and code reading from an io.Reader checks for it with errors.Is(err, io.EOF) to distinguish done from a real failure.

Example: Sentinel Errors in the Standard Library

markup
package main

import (
	"errors"
	"fmt"
	"io"
	"strings"
)

func main() {
	r := strings.NewReader("hi")
	buf := make([]byte, 10)
	_, err := r.Read(buf)
	_, err = r.Read(buf) // second read hits the end
	if errors.Is(err, io.EOF) {
		fmt.Println("reached end of stream")
	}
}
Common Mistakes
  1. Comparing a sentinel error to a possibly-wrapped error with == instead of errors.Is.
  2. Creating a new errors.New("not found") every time instead of reusing one shared package-level sentinel, breaking equality checks.
  3. Not naming sentinel errors with the conventional Err prefix, making them harder to spot and document.
Chapter Summary
  • A sentinel error is a package-level error value, conventionally named ErrSomething, created once with errors.New.
  • Callers check for it with errors.Is(err, ErrSomething), which also works through wrapped errors.
  • Sentinel errors act as a small, stable public API for signaling specific known failure conditions.
  • The standard library uses this pattern widely, e.g. io.EOF and sql.ErrNoRows.
🔒

Chapter Quiz — Complete all 6 topics to unlock

0/6 topics done

Complete these topics first:

Login to run this code

C/C++/Java/PHP execution requires a free account. Your code is saved — you'll land right back in the editor after logging in.