Skip to content

errors

Errors for Go with no dependencies — stack traces, user-facing hints, slog-native structured attributes, and an aggregate that does not lose them.

go get gitlab.com/phpboyscout/go/errors

It imports nothing

Not "few dependencies" — none, outside the standard library. That is asserted by a test in the package rather than left to review, because an error package is imported by every module in the estate. It should be the lightest thing in the dependency graph, not the heaviest.

What it gives you

import "gitlab.com/phpboyscout/go/errors"

// A sentinel: no stack, and a stable kind so its identity survives a wire.
var ErrNotFound = errors.NewSentinel("forge.not_found", "provider not found")

func load(name string) error {
    if name == "" {
        return errors.WithHint(
            errors.WithStack(ErrNotFound),
            "Name a registered provider, or register one with forge.Register().",
        )
    }

    return errors.Wrapf(doLoad(name), "loading %s", name)
}

Reading one back:

Call Gives you
errors.Hints(err) what the user should do about it
errors.Attrs(err) []slog.Attr — for a log record or a span
errors.Details(err) operator-facing prose
errors.StackOf(err) the outermost stack, renderable
errors.AsType[*MyErr](err) typed extraction with no out-parameter
fmt.Printf("%+v", err) message, hints, details, attributes, stack

Three things worth knowing early

Sentinels do not carry a stack. New captures one at the call site, which is right everywhere except a package-level var — there the call site is package initialisation. Use NewSentinel.

Join behaves. It satisfies Unwrap() []error directly, as the standard library does, so hints and attributes attached below it stay readable. See Aggregate errors.

Errors log as structure, not strings. They implement slog.LogValuer, so logger.Error("failed", "err", err) emits a group rather than a flattened line. See Log an error.

Coming from cockroachdb/errors

The names and signatures match, so migrating a module is an import-path change rather than a port. There are three behaviour differences worth reading before you start — see Migrate from cockroachdb/errors, and Why we own this for how we got here.