Error Wrapping
A pattern for adding context to errors while preserving the original error for inspection.
Contributors
When to Use
- When you need to add context to errors as they propagate up the call stack
- When you want to preserve the original error for programmatic inspection
- When debugging requires understanding the full error chain
When NOT to Use
- For simple programs where error context isn't needed
- When exposing internal error details would be a security concern
Error wrapping in Go allows you to add context to errors while preserving the original error. Since Go 1.13, the standard library provides fmt.Errorf with the %w verb and the errors.Is/errors.As functions for working with wrapped errors.
Implementation
package repository
import (
"errors"
"fmt"
)
var ErrNotFound = errors.New("not found")
type User struct {
ID int
Name string
}
type UserRepository struct {
db map[int]User
}
func (r *UserRepository) GetByID(id int) (User, error) {
user, ok := r.db[id]
if !ok {
// Wrap sentinel error with context
return User{}, fmt.Errorf("user %d: %w", id, ErrNotFound)
}
return user, nil
}
func (r *UserRepository) UpdateName(id int, name string) error {
user, err := r.GetByID(id)
if err != nil {
// Wrap error with additional context
return fmt.Errorf("updating name: %w", err)
}
user.Name = name
r.db[id] = user
return nil
}
Usage
package main
import (
"errors"
"fmt"
)
func main() {
repo := &UserRepository{db: make(map[int]User)}
err := repo.UpdateName(42, "Alice")
if err != nil {
// Check for specific error type
if errors.Is(err, ErrNotFound) {
fmt.Println("User does not exist")
}
// Full error message includes all context
// Output: "updating name: user 42: not found"
fmt.Println(err)
}
}
// Unwrapping to access the original error
func handleError(err error) {
var notFound *NotFoundError
if errors.As(err, ¬Found) {
fmt.Printf("Resource %s not found\n", notFound.Resource)
}
}
Benefits
- Preserves error chain: Original errors remain accessible via
errors.Isanderrors.As - Adds debugging context: Each layer can add relevant information
- Standard library support: No external dependencies required
- Backwards compatible: Works with existing error handling code
- Enables precise error handling: Callers can check for specific error types at any level