Go Error Wrapping

Here’s how to wrap errors in Go using the fmt.Errorf() function with the %w verb:

import (
    "fmt"
    "errors"
)

func doSomething() error {
    err := someOperation()
    if err != nil {
        return fmt.Errorf("failed to do something: %w", err)
    }
    return nil
}

To check if an error is a specific type, you can use:

var originalErr *MyCustomError
if errors.As(err, &originalErr) {
    // Handle the specific error type
}

To check if an error wraps a specific error value:

if errors.Is(err, sql.ErrNoRows) {
    // Handle the specific error
}

Here’s a more complete example showing error wrapping in practice:

type QueryError struct {
    Query string
    Err   error
}

func (e *QueryError) Error() string {
    return fmt.Sprintf("query error: %s: %v", e.Query, e.Err)
}

func (e *QueryError) Unwrap() error {
    return e.Err
}

func queryDatabase(query string) error {
    err := db.Execute(query)
    if err != nil {
        return &QueryError{
            Query: query,
            Err:   err,
        }
    }
    return nil
}

func handleRequest() error {
    if err := queryDatabase("SELECT * FROM users"); err != nil {
        return fmt.Errorf("failed to handle request: %w", err)
    }
    return nil
}

Key points about error wrapping:

  • Use %w verb with fmt.Errorf() to wrap errors
  • errors.Unwrap() returns the wrapped error
  • errors.Is() checks for specific error values through the entire wrap chain
  • errors.As() helps with type assertions through the wrap chain
  • Custom error types can implement Unwrap() method to support wrapping

Would you like me to explain any particular aspect of error wrapping in more detail?