sentinelerrors
About 348 wordsAbout 1 min
2025-01-16
Prefers sentinel errors over inline errors.New().
Category
Error Handling
What It Checks
This analyzer detects inline error creation that should be sentinel errors.
Why It Matters
Inline errors can't be checked programmatically:
// Callers can't check for this specific error
if err := doThing(); err != nil {
return errors.New("not found") // New error every time
}
// Caller code:
if err.Error() == "not found" { // Fragile string comparison!
// handle not found
}Sentinel errors enable proper error handling:
var ErrNotFound = errors.New("not found")
func doThing() error {
return ErrNotFound // Same error instance
}
// Caller code:
if errors.Is(err, ErrNotFound) { // Robust check
// handle not found
}Examples
Bad
func GetUser(id string) (*User, error) {
user := db.Find(id)
if user == nil {
return nil, errors.New("user not found") // Inline error
}
return user, nil
}
func GetOrder(id string) (*Order, error) {
order := db.Find(id)
if order == nil {
return nil, errors.New("order not found") // Different message, same concept
}
return order, nil
}Good
// Define sentinel errors at package level
var (
ErrUserNotFound = errors.New("user not found")
ErrOrderNotFound = errors.New("order not found")
)
func GetUser(id string) (*User, error) {
user := db.Find(id)
if user == nil {
return nil, ErrUserNotFound
}
return user, nil
}
func GetOrder(id string) (*Order, error) {
order := db.Find(id)
if order == nil {
return nil, ErrOrderNotFound
}
return order, nil
}Naming Convention
Sentinel errors should:
- Be exported (start with capital letter)
- Start with
Err - Be descriptive
var (
ErrNotFound = errors.New("not found")
ErrUnauthorized = errors.New("unauthorized")
ErrInvalidInput = errors.New("invalid input")
ErrAlreadyExists = errors.New("already exists")
)Wrapping Sentinels
You can add context while preserving the sentinel:
if user == nil {
return nil, fmt.Errorf("get user %s: %w", id, ErrNotFound)
}
// Caller can still check:
if errors.Is(err, ErrNotFound) {
// handle
}Configuration
# .golint-sl.yaml
analyzers:
sentinelerrors: true # enabled by defaultWhen to Disable
- One-off errors that are never checked
- Errors with dynamic content that can't be sentinels
analyzers:
sentinelerrors: falseRelated Analyzers
- errorwrap - Error context wrapping
- humaneerror - User-facing errors
