October DealsAmazon USOctober deal check: compare before you payAmazon US: current deals, useful picks and tech finds.Check DealsPC HealthRecommendedCrashes, freezes, slowdowns? Check your PC nowSpot repairable issues before they interrupt work.Check PCOctober DealsAmazon USDeal season is back - check today's better picksAmazon US: current deals, useful picks and tech finds.See Picks×
Skip to content
World desk4 min

Go Error Handling: When to Wrap, Match, or Hide a Cause

Go’s error-handling tools make a key API choice explicit: expose an underlying cause with %w, or keep it private while adding context. Learn how sentinels, typed errors, errors.Is, errors.As, and errors.Join fit together.

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

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

In Go, choose error handling based on what callers should be able to learn: use %w when the underlying error is part of your API, %v when you want context without exposing that error for inspection, errors.Is to match a condition, and errors.As to retrieve a structured error. For independent failures, Go 1.20 and later also provide errors.Join.

What wrapping promises to callers

An error is an interface value. A wrapper adds context while exposing an underlying error through an Unwrap() error method. The standard inspection functions can follow that relationship, so callers need not assume the returned error is the original value or appears at a particular wrapper depth.

As an Amazon Associate I earn from qualifying purchases.

For example, when loading a named configuration file:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
if err != nil {
    return fmt.Errorf("load config %q: %w", name, err)
}

The added text helps identify the failed operation. The %w also makes the underlying error inspectable. As Go authors Damien Neil and Jonathan Amsterdam put it, “Wrapping an error makes that error part of your API.” (Go Blog, “Working with Errors in Go 1.13”)

That is why wrapping is an API design choice, not just a formatting choice. If callers are meant to rely on a condition or type, document it and preserve that promise consistently. If the cause is an implementation detail, exposing it can constrain future changes.

When to expose a cause—and when to hide it

Expose errors callers supplied

If a function accepts an io.Reader, a read failure comes from a dependency supplied by the caller. Letting the caller inspect that failure may be useful. Wrapping it with %w adds operation context while retaining the caller’s ability to identify the underlying error.

Keep internal implementation details private

A package that uses a database internally may not want callers to depend on a database-specific error such as sql.ErrNoRows. If the package wraps and exposes that sentinel, callers may build behavior around it. Replacing the database later could then break those callers even if the package’s intended abstraction has not changed.

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

When the underlying cause should remain private, %v can include its rendered message without making it available through unwrapping:

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

The visible text can resemble the %w version, but the inspection contract differs. You can also translate a dependency failure into an error that represents your package’s own stable contract.

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

Keep ordinary nil checks as they are: if err != nil still checks whether a call returned an error. When matching an error that may be wrapped, use errors.Is rather than equality. The Go FAQ recommends this change for wrapped errors. (Go error values FAQ)

Use a sentinel for a stable condition

A sentinel is a package-level error value callers can recognize, such as a documented “not found” condition. The package can add context while wrapping it:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
return fmt.Errorf("load config %q: %w", name, ErrNotFound)

Callers should test the promised condition with errors.Is:

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

errors.Is checks whether the error or an error it wraps matches the target. This avoids relying on direct equality with the returned wrapper.

Use a typed error for structured details

When callers need data such as a path, query, or field, a structured error type can communicate that information. Use errors.As to find a matching type through wrappers rather than asserting the type of the top-level returned value:

var pathErr *PathError
if errors.As(err, &pathErr) {
    fmt.Println(pathErr.Path)
}

Document which types callers may rely on. Undocumented concrete error values can become accidental API commitments just as easily as exposed dependency sentinels.

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

How to choose the error behavior

Caller need Approach Contract effect
Add context and let callers inspect the cause Wrap with %w The underlying condition or type is part of the API; document what callers may inspect.
Add context but keep the cause private Format with %v or translate the failure Callers receive context without an unwrap path to that dependency error.
Recognize a stable condition Use a sentinel and match with errors.Is The documented condition remains recognizable through wrappers.
Read structured error details Use an error type and find it with errors.As The documented type and its relevant fields become part of the caller-facing contract.
Report independent failures together Use errors.Join when Go 1.20 or later is supported The returned error represents multiple underlying errors, not one linear cause.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

When multiple failures should be joined

Go 1.20 expanded error wrapping to support multiple underlying errors. A custom error can implement Unwrap() []error; fmt.Errorf can use multiple %w verbs; and errors.Join combines supplied non-nil errors into an error that wraps them. errors.Is and errors.As inspect this multi-error structure. (See the Go 1.20 release notes and errors package documentation.)

Join errors when a single operation has independent failures worth reporting together. A joined error is a tree of underlying errors, not a chain with one cause at each step. Callers can test for a matching condition or type in that tree, so the contract should explain what matches are meaningful.

Design the contract before choosing the format verb

  • Decide whether the underlying cause belongs to the caller-facing abstraction or is an internal implementation detail.
  • For stable conditions, document the sentinel and expect callers to use errors.Is.
  • For structured information, document the error type and relevant fields callers can retrieve with errors.As.
  • Use %w only when exposing the underlying error is intentional; otherwise use %v or translate the failure.
  • Use errors.Join for independent failures, and describe the multi-error matching behavior callers can rely on.

Go’s 2025 discussion of error-handling syntax notes a longstanding complaint about verbosity: “One of the oldest and most persistent complaints about Go concerns the verbosity of error handling.” — Robert Griesemer, Go Blog. That complaint is about the language’s error-handling syntax; it is not a measured finding about wrapping or error-handling outcomes.

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.

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

Leave a Reply

Your email address will not be published. Required fields are marked *

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

More from the Wire

  1. World desk4 min
    How to Spot an AI Voice Scam Before Sending MoneyDon’t rely on how a caller sounds. Pause, call back through a known number, and verify the emergency with another trusted person before sending money.
  2. Mountain View desk4 min
    Google’s SynthID Detector: How to Check AI-Generated Images, Video and AudioGoogle’s SynthID Detector looks for an embedded watermark in supported images, video and audio. Here is what its results do—and do not—show.
  3. Redmond desk20 min
    How to create a link to File or Folder in Windows 11Windows 11 gives you several ways to point to a file or folder without moving or duplicating it. You can create a desktop shortcut,…
Recommended PC Tool
Recommended PC Tool
Windows Errors? Fix Them Before They SpreadFree repair scan
Crashes, No Sound, or Screen Glitches?Free driver scan

Two free Windows tools

One Free Minute Could Fix That PC

Before you go - each of these free tools takes about a minute and tackles what quietly slows a Windows PC down.

Special offer. View Outbyte info, uninstall instructions, EULA, and Privacy Policy.