What this package does not do¶
Deliberate absences and known constraints. Each is a decision rather than a gap waiting to be filled, except where it says otherwise.
No OpenTelemetry, and no RecordError¶
This package ships nothing that talks to OTEL.
Deciding an error is worth recording happens where a span is in scope, not where
the error is made. That belongs to gitlab.com/phpboyscout/go/observability,
which reaches what it needs through StackOf,
KindOf and Attrs — the reason those
are exported and renderable.
No wire serialisation — yet¶
There is no encoder or decoder here. A sibling module is planned, keyed on
ErrorKind, which is why kinds are stable strings and why the
package documents them as serialisation keys.
Until it exists, an error that crosses a process boundary arrives as whatever the transport made of it, and its identity has to be re-established by the receiver.
No dependencies, and that constrains the API¶
The package imports only the standard library, asserted by depfootprint_test.go
rather than left to review. An error package is imported by every module in the
estate, so it should be the lightest thing in the graph.
The visible consequence is WithAttrs taking
[]slog.Attr rather than a richer type of its own.
Stacks are bounded at 32 frames¶
Deep enough for any real call chain, shallow enough that an error on a hot path is not expensive. A chain deeper than that is truncated, and there is no option to raise the bound.
Errors are immutable once constructed¶
Every annotator returns a new error wrapping the old one. Nothing hands back a
mutable map for you to add to later, which is how tozd/go/errors does it —
mutating an error after construction invites aliasing and races.
A sentinel carries no stack until you add one¶
NewSentinel captures no stack by design, because
at package scope the call site is initialisation. If you return a sentinel
directly, the error has no stack at all. Wrap it:
Is compares pointers, which do not survive a wire¶
Is matches on identity, so a sentinel decoded from a transport is not the same
instance as the one in your binary and will not match. The stable
kind is what a future codec will use to re-establish identity.
A leaf from Newf has no kind¶
KindOf returns "" for an error built from New, Newf
or Errorf that does not wrap a sentinel, and a slog record of it therefore has
no kind field. This is a consequence of identity being something you declare,
not something inferred from a message — but it does mean an error you want to
alert on needs a sentinel at its leaf.
Hints and details de-duplicate; attributes do not¶
Hints and Details collapse identical strings
to one. Attrs keeps every attribute, including repeated
keys, because which one wins is the caller's decision.
If you attach the same hint at three levels you will see it once; the same attribute key at three levels appears three times.
Nothing here reports on an error's absence¶
There is no "assert this error was handled" tooling, no linting of unwrapped
returns, and no runtime warning for an error that was constructed and dropped.
That is the job of errcheck and the rest of the lint suite.