Go
Beginner
1 min read
Error Wrapping, Unwrapping, and Sentinel Errors
Example
package main
import (
"errors"
"fmt"
"io/fs"
"os"
)
// Sentinel errors
var (
ErrNotFound = errors.New("not found")
ErrUnauthorised = errors.New("unauthorised")
)
type DBError struct {
Code int
Message string
}
func (e *DBError) Error() string {
return fmt.Sprintf("db error %d: %s", e.Code, e.Message)
}
func queryUser(id int) error {
if id <= 0 {
return fmt.Errorf("queryUser: %w", ErrNotFound)
}
if id == 999 {
return fmt.Errorf("queryUser: %w", &DBError{Code: 500, Message: "connection lost"})
}
return nil
}
func main() {
// errors.Is — checks the chain
err := queryUser(0)
fmt.Println(errors.Is(err, ErrNotFound)) // true
// errors.As — extracts a specific type from the chain
err = queryUser(999)
var dbErr *DBError
if errors.As(err, &dbErr) {
fmt.Printf("DB code=%d msg=%s\n", dbErr.Code, dbErr.Message)
}
// Standard library sentinel error
_, err = os.Open("/nonexistent/file.txt")
if errors.Is(err, fs.ErrNotExist) {
fmt.Println("file does not exist (sentinel matched)")
}
// Wrapping chain
base := errors.New("base error")
w1 := fmt.Errorf("layer 1: %w", base)
w2 := fmt.Errorf("layer 2: %w", w1)
fmt.Println("Is base:", errors.Is(w2, base)) // true through chain
fmt.Println("chain:", w2)
}