How to wrap errors

Use `fmt.Errorf` with the `%w` verb to wrap errors, which preserves the original error's type and message while adding context.

Use fmt.Errorf with the %w verb to wrap errors, which preserves the original error's type and message while adding context. This allows you to later inspect the full error chain using errors.Is or errors.As instead of relying on fragile string matching.

Here is a practical example showing how to wrap an error during file operations and then unwrap it to check for specific conditions:

package main

import (
	"errors"
	"fmt"
	"os"
)

var ErrNotFound = errors.New("file not found")

func readFile(path string) error {
	_, err := os.Open(path)
	if err != nil {
		// Wrap the error with context. The %w verb is crucial here.
		return fmt.Errorf("failed to read config: %w", err)
	}
	return nil
}

func main() {
	err := readFile("missing.txt")
	if err != nil {
		// Check if the root cause is a specific error type
		if errors.Is(err, os.ErrNotExist) {
			fmt.Println("File does not exist, creating default...")
		} else if errors.Is(err, ErrNotFound) {
			fmt.Println("Custom not found error detected")
		} else {
			// Print the full chain of errors
			fmt.Printf("Unexpected error: %v\n", err)
		}
	}
}

When you wrap an error, the fmt.Errorf function creates a new error that holds a reference to the original. If you print this error, Go automatically formats the chain (e.g., failed to read config: open missing.txt: no such file or directory). However, for programmatic checks, you must use the errors package. errors.Is traverses the chain to see if any error in the chain matches a target error value, while errors.As attempts to extract a specific error type from the chain.

Avoid wrapping errors with simple string concatenation (e.g., fmt.Errorf("error: %s", err)). This breaks the error chain, making it impossible to use errors.Is or errors.As later. If you need to add context without wrapping (for example, when the error is already a custom type you want to preserve exactly), you can create a custom error type that implements the Unwrap() method, but for 95% of cases, fmt.Errorf with %w is the standard and most robust approach.