Documentation Index
Fetch the complete documentation index at: https://mintlify.com/golang/go/llms.txt
Use this file to discover all available pages before exploring further.
The errors package implements functions to manipulate errors. It provides functionality for creating, wrapping, and inspecting errors.
Creating Errors
Basic Error Creation
import "errors"
// Create a simple error
var ErrNotFound = errors.New("resource not found")
func findUser(id int) error {
if id < 0 {
return errors.New("invalid user ID")
}
return nil
}
import "fmt"
func validateAge(age int) error {
if age < 0 {
return fmt.Errorf("invalid age: %d", age)
}
return nil
}
Error Wrapping
Wrapping with %w
func readConfig(filename string) error {
data, err := os.ReadFile(filename)
if err != nil {
return fmt.Errorf("failed to read config: %w", err)
}
// Process data...
return nil
}
Multiple Error Wrapping (Go 1.20+)
func multipleErrors() error {
err1 := errors.New("first error")
err2 := errors.New("second error")
return fmt.Errorf("multiple failures: %w, %w", err1, err2)
}
Error Inspection
errors.Is
Check if an error matches a target error.
import (
"errors"
"io/fs"
)
func handleError(err error) {
if errors.Is(err, fs.ErrNotExist) {
fmt.Println("File does not exist")
} else if errors.Is(err, fs.ErrPermission) {
fmt.Println("Permission denied")
} else {
fmt.Printf("Other error: %v\n", err)
}
}
func example() {
_, err := os.Open("nonexistent.txt")
if err != nil {
handleError(err)
}
}
errors.As
Extract a specific error type from an error chain.
import "os"
func examineError(err error) {
var pathErr *os.PathError
if errors.As(err, &pathErr) {
fmt.Printf("Operation: %s\n", pathErr.Op)
fmt.Printf("Path: %s\n", pathErr.Path)
fmt.Printf("Error: %v\n", pathErr.Err)
}
}
errors.Unwrap
Unwrap a wrapped error.
func unwrapExample(err error) {
unwrapped := errors.Unwrap(err)
if unwrapped != nil {
fmt.Printf("Unwrapped: %v\n", unwrapped)
}
}
Custom Error Types
Simple Custom Error
type ValidationError struct {
Field string
Value interface{}
Msg string
}
func (e *ValidationError) Error() string {
return fmt.Sprintf("%s validation failed: %s (value: %v)", e.Field, e.Msg, e.Value)
}
func validateEmail(email string) error {
if !strings.Contains(email, "@") {
return &ValidationError{
Field: "email",
Value: email,
Msg: "must contain @",
}
}
return nil
}
Custom Error with Wrapping
type DatabaseError struct {
Query string
Err error
}
func (e *DatabaseError) Error() string {
return fmt.Sprintf("database query failed: %s", e.Query)
}
func (e *DatabaseError) Unwrap() error {
return e.Err
}
func executeQuery(query string) error {
err := errors.New("connection timeout")
if err != nil {
return &DatabaseError{
Query: query,
Err: err,
}
}
return nil
}
Error with Is Support
type NotFoundError struct {
Resource string
ID int
}
func (e *NotFoundError) Error() string {
return fmt.Sprintf("%s with ID %d not found", e.Resource, e.ID)
}
var ErrNotFound = errors.New("not found")
func (e *NotFoundError) Is(target error) bool {
return target == ErrNotFound
}
func findResource(id int) error {
return &NotFoundError{Resource: "user", ID: id}
}
func handleNotFound() {
err := findResource(123)
if errors.Is(err, ErrNotFound) {
fmt.Println("Resource not found")
}
}
Practical Examples
Multi-Error Accumulation
type MultiError struct {
Errors []error
}
func (m *MultiError) Error() string {
var messages []string
for _, err := range m.Errors {
messages = append(messages, err.Error())
}
return strings.Join(messages, "; ")
}
func (m *MultiError) Add(err error) {
if err != nil {
m.Errors = append(m.Errors, err)
}
}
func (m *MultiError) HasErrors() bool {
return len(m.Errors) > 0
}
func validateUser(user User) error {
var errs MultiError
if user.Name == "" {
errs.Add(errors.New("name is required"))
}
if user.Email == "" {
errs.Add(errors.New("email is required"))
}
if user.Age < 0 {
errs.Add(errors.New("age must be positive"))
}
if errs.HasErrors() {
return &errs
}
return nil
}
Error Chain Walking
func printErrorChain(err error) {
for err != nil {
fmt.Printf("Error: %v\n", err)
err = errors.Unwrap(err)
}
}
func example() {
err := fmt.Errorf("outer: %w",
fmt.Errorf("middle: %w",
errors.New("inner error")))
printErrorChain(err)
// Output:
// Error: outer: middle: inner error
// Error: middle: inner error
// Error: inner error
}
Retry with Error Context
func retryOperation(op func() error, maxRetries int) error {
var lastErr error
for i := 0; i < maxRetries; i++ {
err := op()
if err == nil {
return nil
}
lastErr = fmt.Errorf("attempt %d/%d: %w", i+1, maxRetries, err)
time.Sleep(time.Second * time.Duration(i+1))
}
return fmt.Errorf("operation failed after %d retries: %w", maxRetries, lastErr)
}
Sentinel Errors
var (
ErrInvalidInput = errors.New("invalid input")
ErrUnauthorized = errors.New("unauthorized")
ErrResourceExpired = errors.New("resource expired")
)
func processRequest(req Request) error {
if req.Token == "" {
return ErrUnauthorized
}
if req.Data == nil {
return ErrInvalidInput
}
// Process...
return nil
}
func handleRequest(req Request) {
err := processRequest(req)
switch {
case errors.Is(err, ErrUnauthorized):
// Handle auth error
case errors.Is(err, ErrInvalidInput):
// Handle validation error
case err != nil:
// Handle other errors
}
}
Error with Context Information
type HTTPError struct {
StatusCode int
Message string
Err error
}
func (e *HTTPError) Error() string {
if e.Err != nil {
return fmt.Sprintf("%d %s: %v", e.StatusCode, e.Message, e.Err)
}
return fmt.Sprintf("%d %s", e.StatusCode, e.Message)
}
func (e *HTTPError) Unwrap() error {
return e.Err
}
func makeRequest(url string) error {
resp, err := http.Get(url)
if err != nil {
return &HTTPError{
StatusCode: 0,
Message: "request failed",
Err: err,
}
}
defer resp.Body.Close()
if resp.StatusCode != 200 {
return &HTTPError{
StatusCode: resp.StatusCode,
Message: "unexpected status",
}
}
return nil
}
Error Handling Patterns
Early Return
func processData(data []byte) error {
if len(data) == 0 {
return errors.New("empty data")
}
parsed, err := parse(data)
if err != nil {
return fmt.Errorf("parse failed: %w", err)
}
if err := validate(parsed); err != nil {
return fmt.Errorf("validation failed: %w", err)
}
return save(parsed)
}
Error Decoration
func decorateError(operation string, err error) error {
if err == nil {
return nil
}
return fmt.Errorf("%s failed: %w", operation, err)
}
func workflow() error {
if err := step1(); err != nil {
return decorateError("step1", err)
}
if err := step2(); err != nil {
return decorateError("step2", err)
}
return nil
}
Panic Recovery with Error
func safeExecute(fn func() error) (err error) {
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("panic recovered: %v", r)
}
}()
return fn()
}
Best Practices
- Use error wrapping - Preserve error context with
%w
- Create sentinel errors - Define package-level error variables for common errors
- Use errors.Is and errors.As - Don’t compare errors with
== directly
- Provide context - Add information about what was being done when the error occurred
- Don’t ignore errors - Always handle or explicitly ignore with
_ = err
- Custom error types - Create custom types for errors with additional information
- Error messages - Start with lowercase, no punctuation at end
- Wrap errors once - Don’t wrap the same error multiple times unnecessarily
Common Patterns
Error Variable Naming
var (
ErrNotFound = errors.New("not found")
ErrAlreadyExists = errors.New("already exists")
ErrInvalidFormat = errors.New("invalid format")
)
Error Type Naming
type ValidationError struct { /* ... */ }
type NetworkError struct { /* ... */ }
type ConfigError struct { /* ... */ }
errors.Join (Go 1.20+)
Combine multiple errors into one.
func processAll(items []Item) error {
var errs []error
for _, item := range items {
if err := process(item); err != nil {
errs = append(errs, err)
}
}
return errors.Join(errs...)
}