Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

iTechGuides is reader-supported. When you buy through links on our site, we may earn an affiliate commission. As an Amazon Associate I earn from qualifying purchases. Learn more

In Go, wrapping an error with %w does more than add context: it lets callers inspect the underlying error, making that error part of your package’s API. Use errors.Is to recognize a documented condition such as a sentinel, errors.As to retrieve a documented error type, and %v when you want context without exposing the cause for inspection.

What wrapping promises to callers

Go errors are values that satisfy the error interface. A wrapper adds context and exposes an underlying error through an Unwrap() error method. With fmt.Errorf, the %w verb creates a wrapper that Go’s error inspection functions can traverse.

if err != nil {
    return fmt.Errorf("load config %q: %w", name, err)
}

The message gives a person useful context about the failed operation. The %w also allows callers to inspect the underlying error, even if your function adds more wrappers later. As Go authors Damien Neil and Jonathan Amsterdam put it, “Wrapping an error makes that error part of your API.” Go’s Go 1.13 error guidance explains the contract and its trade-offs.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

If callers should see the message but not inspect the cause, use %v instead:

return fmt.Errorf("load config %q: %v", name, err)

The rendered text may look the same with either verb, but only %w exposes an unwrap path. Choose based on the package contract, not merely on how the error prints.

When to expose a dependency’s error

Expose an underlying error when it is useful to the caller and belongs at the package boundary. For example, if a function accepts an io.Reader from its caller, preserving a read error may be appropriate: the caller supplied the reader and may need to identify its failure.

Be more cautious with dependencies your package uses internally. If a package talks to a database behind its own abstraction, wrapping a database-specific condition such as sql.ErrNoRows can bind callers to that implementation. A later database replacement could then break callers that rely on the old sentinel, even if the package’s own documented purpose has not changed.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Document the error properties callers may rely on—such as a sentinel condition or a specific type—and keep other implementation details private. If you promise that callers can match a condition, make sure every relevant return path preserves that promise.

How to recognize a sentinel through wrappers

A sentinel is a package-level error value that represents a stable condition, such as “not found.” Callers should use errors.Is rather than equality when the returned error may be wrapped:

if errors.Is(err, ErrNotFound) {
    // handle the documented condition
}

errors.Is checks the error’s wrapped structure, so callers do not need to know how many layers of context were added. The Go FAQ recommends replacing equality checks with errors.Is when wrapped errors must match. A basic err != nil check still works as before. The Go error-value FAQ covers the change.

How to retrieve a structured error type

Use a typed error when callers need structured details—such as a path, query, or field—not just a yes-or-no condition. Call errors.As to find a matching type through wrapping:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
var pathErr *PathError
if errors.As(err, &pathErr) {
    fmt.Println(pathErr.Path)
}

The target passed to errors.As is a pointer to a value of the type you want to retrieve. This lets your package add context without requiring callers to assert that the returned error itself has a particular concrete type. Document which types are stable; do not make callers depend on undocumented implementation values. See the Go errors package documentation for the matching behavior.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Choosing the right error behavior

Need Pattern What callers can rely on
Add context and permit inspection fmt.Errorf("...: %w", err) Use errors.Is or errors.As to inspect the exposed error structure.
Add context but keep a dependency detail private fmt.Errorf("...: %v", err), or translate the failure The message can include the cause, but callers cannot unwrap it through this formatting operation.
Expose a stable condition Wrap a documented sentinel Use errors.Is rather than equality.
Expose structured details Return or wrap a documented error type Use errors.As rather than a direct assertion on the returned value.
Report independent failures together errors.Join or another multi-error Inspection traverses multiple underlying errors rather than one linear chain.

When several failures occur: errors.Join

Sometimes an operation has multiple independent failures worth reporting together—for example, when cleanup produces an additional error after the main operation fails. Go 1.20 added multi-error wrapping: a custom error can implement Unwrap() []error, fmt.Errorf can contain multiple %w verbs, and errors.Join combines supplied non-nil errors. errors.Is and errors.As inspect the resulting multi-error tree.

Use a joined error when callers benefit from seeing the separate failures, not simply to create a longer message. It is a tree of causes, not a single chain; explain which conditions callers may match and avoid implying that one failure is necessarily the cause of another. These features are documented in the Go 1.20 release notes and the errors package documentation. errors.Join requires Go 1.20 or later.

“How should I change my error-handling code to work with the new features?”

For wrapped errors, replace comparisons such as err == ErrNotFound with errors.Is(err, ErrNotFound). For a wrapped concrete error type, use errors.As instead of asserting directly on the returned error. Keep err != nil checks; they do not need to change. This is the guidance in the Go error-value FAQ.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.