← Back to Go Course | Chapter 9: Error Handling | Lesson 6 of 6

Wrapping Errors

Wrapping an error is like putting the original problem inside a new envelope with extra notes on the outside, while still letting someone open it back up to see the original note inside.
Syntax
go
return fmt.Errorf("context: %w", err)

errors.Unwrap(err)

Wrapping with %w

fmt.Errorf's %w verb, used in place of %v or %s, wraps the given error inside a new one, adding a message prefix while preserving the ability to unwrap back to the original.

Example: Wrapping with %w

markup
// Every Go file belongs to a package; main builds an executable
package main

// Import the packages this program uses
import (
	"errors"
	"fmt"
)

// Define the function readConfig
func readConfig() error {
	// Send errors.New("file missing") back to the caller
	return errors.New("file missing")
}

// Define the function startApp
func startApp() error {
	// Check whether err := readConfig(); err != nil
	if err := readConfig(); err != nil {
		return fmt.Errorf("startApp: %w", err)
	}
	// Send nil back to the caller
	return nil
}

// main is where the program starts running
func main() {
	fmt.Println(startApp())
}

Checking Through a Wrapped Chain

Even after several layers of wrapping, errors.Is can still detect the original error at the bottom of the chain, letting each layer add context without breaking error identity checks further up the call stack.

Example: Checking Through a Wrapped Chain

markup
// Every Go file belongs to a package; main builds an executable
package main

// Import the packages this program uses
import (
	"errors"
	"fmt"
)

var ErrConfigMissing = errors.New("config missing")

// Define the function readConfig
func readConfig() error {
	// Send ErrConfigMissing back to the caller
	return ErrConfigMissing
}

// Define the function startApp
func startApp() error {
	// Check whether err := readConfig(); err != nil
	if err := readConfig(); err != nil {
		return fmt.Errorf("startApp failed: %w", err)
	}
	// Send nil back to the caller
	return nil
}

// main is where the program starts running
func main() {
	err := startApp()
	fmt.Println(err)
	fmt.Println("is config missing:", errors.Is(err, ErrConfigMissing))
}

Manually Unwrapping

errors.Unwrap retrieves the single error wrapped one level down, which is the primitive that errors.Is and errors.As use internally to walk the whole chain step by step.

Note: Prefer errors.Is/errors.As in application code -- Unwrap is mostly useful for writing your own error-inspection tools.

Example: Manually Unwrapping

markup
// Every Go file belongs to a package; main builds an executable
package main

// Import the packages this program uses
import (
	"errors"
	"fmt"
)

// main is where the program starts running
func main() {
	// Short declaration: create base and infer the type
	base := errors.New("disk full")
	// Short declaration: create wrapped and infer the type
	wrapped := fmt.Errorf("save failed: %w", base)

	inner := errors.Unwrap(wrapped)
	// Print the values followed by a newline
	fmt.Println("outer:", wrapped)
	// Print the values followed by a newline
	fmt.Println("inner:", inner)
}
Related Topics
{# common_mistakes/chapter_summary/browser_support: on Hindi pages the view already swaps in the hi_ translation fields (or blanks these out if untranslated), so this renders correctly for both languages without a lang_code check here. #}
Common Mistakes
  1. Using %v instead of %w in fmt.Errorf, which loses the ability for errors.Is/errors.As to see the original wrapped error.
  2. Wrapping the same error many times through several layers, creating an error message so long it becomes unreadable.
  3. Forgetting that wrapping doesn't change what errors.Is/errors.As see -- they still find the originally wrapped sentinel or type.
Chapter Summary
  • fmt.Errorf's %w verb wraps an existing error while adding additional context.
  • A wrapped error still satisfies errors.Is/errors.As checks against the original error.
  • Wrapping preserves the full chain of context as an error travels up through several function calls.
  • Errors.Unwrap can manually retrieve the next error in a wrap chain, though Is/As are usually preferred.
🔒

Chapter Quiz — Complete all 6 topics to unlock

0/6 topics done

Complete these topics first:

Login to run this code

C/C++/Java/PHP execution requires a free account. Your code is saved — you'll land right back in the editor after logging in.