patterngoModeratepending
Go error wrapping and inspection patterns
Viewed 0 times
error wrappingerrors.Iserrors.Asfmt.Errorfsentinel errors
Problem
Need to add context to errors while preserving the ability to check for specific error types.
Solution
Go 1.13+ error wrapping with fmt.Errorf and errors.Is/As:
import (
"errors"
"fmt"
)
// Define sentinel errors
var (
ErrNotFound = errors.New("not found")
ErrPermission = errors.New("permission denied")
ErrValidation = errors.New("validation failed")
)
// Wrap errors with context using %w
func GetUser(id string) (*User, error) {
user, err := db.FindUser(id)
if err != nil {
return nil, fmt.Errorf("GetUser(%s): %w", id, err)
}
return user, nil
}
func GetUserProfile(id string) (*Profile, error) {
user, err := GetUser(id)
if err != nil {
return nil, fmt.Errorf("GetUserProfile: %w", err)
}
// ... build profile
return profile, nil
}
// Check for specific errors through wrapping chain
err := GetUserProfile("123")
if errors.Is(err, ErrNotFound) {
// err was ErrNotFound somewhere in the chain
// Even though it's wrapped: "GetUserProfile: GetUser(123): not found"
}
// Custom error types
type ValidationError struct {
Field string
Message string
}
func (e *ValidationError) Error() string {
return fmt.Sprintf("%s: %s", e.Field, e.Message)
}
// Extract custom error type from chain
var valErr *ValidationError
if errors.As(err, &valErr) {
fmt.Printf("Invalid field: %s\n", valErr.Field)
}
// Multiple wrapping (Go 1.20+)
return fmt.Errorf("operation failed: %w, %w", err1, err2)Why
Error wrapping adds call-site context while errors.Is/As lets callers check for specific errors regardless of how many layers of wrapping exist.
Gotchas
- Use %w (not %v) to make errors unwrappable
- errors.Is checks the entire chain, == only checks the current error
- Don't wrap errors you don't own if the wrapper changes their meaning
Context
Go error handling with context and inspection
Revisions (0)
No revisions yet.