Readers¶
The functions that get something back out of an error.
Every one of them uses the same tree traversal, which visits layers
outermost-first, depth-first through the members of an aggregate. That is why
none of these can disagree with another about what an error contains, and why all
of them see through a Join.
Hints and Details¶
Return every hint (or detail) in the tree, outermost first, de-duplicated.
An empty result is nil, not an empty slice.
err := errors.WithHint(errors.WithHint(errors.WithHint(
errors.New("x"), "inner"), "middle"), "outer")
errors.Hints(err) // [outer middle inner]
Identical strings collapse to one:
Empty strings are dropped rather than returned.
The GetAll aliases¶
Exactly Hints and Details, under the names the estate already called. They
exist so migrating a module is an import-path change rather than a port; prefer
the short names in new code.
FlattenHints and FlattenDetails¶
Join the list into one string, separated by \n--\n:
err := errors.WithHint(errors.WithHint(errors.New("x"), "b"), "a")
errors.FlattenHints(err) // "a\n--\nb"
The separator is deliberately distinctive so a flattened list can be split back out, which a newline-joined one cannot.
Attrs¶
Returns every structured attribute in the tree, outermost first. Duplicate keys
are preserved; see WithAttrs.
KindOf¶
Returns the error's identity: the outermost kind that is not an annotation
wrapper, whether one of this package's own or one a foreign wrapper has
declared through StructuralKinder. Returns ""
when nothing in the tree declares one.
var ErrNotFound = errors.NewSentinel("forge.not_found", "provider not found")
errors.KindOf(ErrNotFound) // "forge.not_found"
errors.KindOf(errors.New("x")) // "errors.basic"
errors.KindOf(fmt.Errorf("plain")) // ""
errors.KindOf(errors.Newf("reading %s", "f")) // ""
Annotating does not change the answer, which is the whole point:
err := errors.WithAttrs(
errors.WithHint(errors.WithStack(ErrNotFound), "h"),
slog.Int("n", 1),
)
errors.KindOf(err) // still "forge.not_found"
The outermost kind alone would report whichever annotation happened to be
applied last (errors.attrs for that error), which describes the package's
plumbing rather than the failure. The five structural kinds skipped are
errors.message, errors.stack, errors.hint, errors.detail and
errors.attrs.
errors.join is not skipped: an aggregate reports errors.join as its
identity.
This is what belongs in a log record's kind field and in OTEL's
exception.type.
StackOf¶
Returns the outermost stack in the tree, or nil. Outermost is the most
recently captured, and so the closest to where the error surfaced. For the
others, see Stacks.
errors.StackOf(ErrNotFound) // nil: a sentinel carries none
errors.StackOf(errors.WithStack(ErrNotFound)) // the stack from the WithStack call
Render it with StackTrace.String().
Stacks, OriginStack and DistinctStacks¶
func Stacks(err error) []StackTrace
func OriginStack(err error) StackTrace
func DistinctStacks(err error) []StackTrace
StackOf answers "where did this surface". These answer "how did it get
here". Every Wrap and Wrapf captures a stack at its own call site, so an
error wrapped three deep carries three, plus the leaf's own if it was made with
New, Newf or Errorf, and StackOf reports only the first it meets.
WithStack adds one only when the error has none yet.
Stacksreturns every stack in the tree, outermost first. Nothing new is captured; it reads all of them rather than stopping at the first.OriginStackreturns the innermost one, the capture closest to where the error was made, ornilwhen there is none.DistinctStacksisStackswith the redundant ones dropped. On a straight call path each outer stack is a suffix of the one captured deeper, so rendering all of them repeats the same frames. A stack survives unless a strictly longer one contains it, compared function by function rather than by program counter. An error created on one path and wrapped from another, or wrapped in a different goroutine, keeps both.
Is, As, AsType and Unwrap¶
func Is(err, target error) bool
func As(err error, target any) bool
func AsType[E error](err error) (E, bool)
func Unwrap(err error) error
Straight passthroughs to the standard library's errors package, so this package
can be a drop-in import without a second import for the basics.
Prefer AsType over As. AsType needs no out-parameter: