Sentinel Errors
In this page:
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
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)
}
Login to try C/C++/Java/PHP code in the editor
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
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")
}
}
Login to try C/C++/Java/PHP code in the editor
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
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")
}
}
Login to try C/C++/Java/PHP code in the editor
- Comparing a sentinel error to a possibly-wrapped error with == instead of errors.Is.
- Creating a new errors.New("not found") every time instead of reusing one shared package-level sentinel, breaking equality checks.
- Not naming sentinel errors with the conventional Err prefix, making them harder to spot and document.
- 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: