go 18 lines · 1 tab

Error wrapping that stays inspectable with errors.Is and errors.As

Leah Thompson Jan 2026
1 tab
package domain

import (
  "errors"
  "fmt"
)

var (
  ErrNotFound = errors.New("not found")
  ErrConflict = errors.New("conflict")
)

func WrapNotFound(resource string, cause error) error {
  if cause == nil {
    return fmt.Errorf("%s: %w", resource, ErrNotFound)
  }
  return fmt.Errorf("%s: %w: %w", resource, ErrNotFound, cause)
}
1 file · go Explain with highlit

In production, you want errors that are both human-readable and machine-checkable. I wrap errors with %w so callers can still match them using errors.Is and errors.As. This avoids string comparisons like if err.Error() == ..., which break on refactors. I also like defining sentinel errors for well-known conditions (ErrNotFound, ErrConflict) and returning them from lower layers with context using fmt.Errorf. The service layer can then map those to HTTP status codes without losing the original cause. The important discipline is consistency: don’t mix ad-hoc errors with sentinel errors for the same condition. If you follow that rule, you get clean logs and predictable error handling across the codebase.


Related snips

Share this code

Here's the card — post it anywhere.

Error wrapping that stays inspectable with errors.Is and errors.As — share card
Link copied