errs

package
v0.1.21 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 21, 2026 License: MIT Imports: 6 Imported by: 0

Documentation

Overview

Package errs — provides package-level introspection helpers that walk a cause chain and read fields from the deepest *Error encountered. Consumers of pkg/v1/errs use re-exports of these.

Package errs — defines the dotted-quad Code type introduced by ADR 0005. Layout: MM.LL.PP.SS over uint32 — Major.Layer.Package.Serial. Codes are comparable, ordered, map-keyable, and const-expressible via hex literals (Pack is a runtime constructor only).

Code is a packed uint32 (shifts/masks only), so it is fully portable across 32- and 64-bit GOARCH — there is no build constraint here. Validation bounds every Code to uint32 ≤ 0x7FFFFFFF (validate.go), so the rare int(code) cast round-trips losslessly even where int is 32-bit.

Package errs — meta-codes (Layer=0 reserved for this file). Used in Define panic messages and in internal bootstrap errors (newValidationError). These codes are documentary: they are NOT returned from emitter APIs as sentinel *Error values.

Layer=0 is ENFORCED reserved for this package by validateDefineArgs — any Define call with Layer==0 AND code not in this whitelist will panic.

Constants are typed as Code so Define call sites accept them directly and downstream comparisons with Error.Code() are type-safe.

Package errs — defines the SDK-wide typed Error plus the Define and Wrap constructors. Consumers never import this package directly — they introspect errors through github.com/kitsunium/sdk/pkg/v1/errs.

Package errs — defines the closed FieldValue union used to attach structured metadata to an Error without opening an `any` back door. FieldValue is a transport + textual-restitution contract, NOT a vehicle for strongly-typed reconstruction on the consumer side — StringValue is the only value accessor exposed in this MR.

Package errs — provides ParseCode, the strict canonical parser for dotted-quad Code strings produced by Code.String(). The parser is intentionally NOT compatible with the Padded() form — that would make two textual representations round-trip to the same Code, violating the single-canonical-form invariant declared in ADR 0005 §3.2.

Package errs — hosts the PrefixMatcher target used by errors.Is for CIDR-style Code matching. PrefixMatcher deliberately does NOT implement the `error` interface so it cannot escape into error chains as a return value — callers pass it only to errors.Is.

Package errs — placeholder companion to the SDK-wide AST audit (registry_external_test.go). There is no runtime registry today; the code allocation table lives in ADR 0002 and the audit test embeds it.

Package errs — holds the wrap-trail mechanism introduced by ADR 0005. Trail carries the sites a *Error was wrapped through; origin wins on Code() per ADR 0002, but Trail() / HasCode() traverse the full list of wrap sites to help observability and matching.

Package errs — TrailOf exposes the wrap-trail codes of the deepest *Error in a chain, the read-side companion to the Of-family accessors, enabling structured log decomposition without reparsing the rendered bracket header.

Package errs — centralises the runtime structural checks applied by Define. Factoring the logic out of Define lets tests cover every rule as a plain function call without subprocess fixtures for package-init panics.

Package errs — hosts WrapParams in its own file so error.go keeps a single exported struct (one-struct-per-file convention).

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func ExitCodeOf

func ExitCodeOf(err error) int

ExitCodeOf walks the chain and returns the deepest *Error.ExitCode().

func HTTPStatusOf

func HTTPStatusOf(err error) int

HTTPStatusOf walks the chain and returns the deepest *Error.HTTPStatus().

func HasCode

func HasCode(err error, c Code) bool

HasCode returns true if any *Error in err's chain has code == c OR any trail entry matches c. The walk recurses through BOTH single-error wrappers (Unwrap() error) AND multi-error wrappers (Unwrap() []error, e.g. errors.Join). Time complexity: O(total_errors + total_trail_lengths).

func HasReason

func HasReason(err error, reason string) bool

HasReason walks the chain and reports whether ANY *Error carries reason.

func PrivateOf

func PrivateOf(err error) string

PrivateOf walks the chain and returns the deepest *Error.Private, or "" when no *Error is present in the chain. DIAGNOSTIC-ONLY: never expose in HTTP/gRPC responses or any user-facing surface. Intended for operator tooling and log correlation.

func PublicOf

func PublicOf(err error) string

PublicOf walks the Unwrap chain and returns the deepest *Error.Public, or "" when no *Error is present in the chain.

func ReasonOf

func ReasonOf(err error) (reason string, ok bool)

ReasonOf walks the Unwrap chain and returns the deepest *Error.Reason.

func RegistryMarker

func RegistryMarker() string

RegistryMarker returns the registry sentinel so external callers (and the audit test itself) can confirm the package is the one expected to own the SDK-wide code allocation audit.

Types

type Code

type Code uint32

Code packs a 4-byte dotted identifier (MM.LL.PP.SS) as uint32. See ADR 0005 for the registry.

const (
	MaskByMajor   Code = 0xFF_00_00_00 // /8 — all codes sharing Major
	MaskByLayer   Code = 0xFF_FF_00_00 // /16 — all codes sharing Major+Layer
	MaskByPackage Code = 0xFF_FF_FF_00 // /24 — all codes sharing Major+Layer+Package
	MaskExact     Code = 0xFF_FF_FF_FF // /32 — exact match only
)

CIDR-style mask constants for prefix matching via NewPrefixMatcher.

const (
	// CodeInvalidCode identifies a Define call whose numeric code is zero,
	// overflows int32-positive range, or violates the Layer=0 reserved rule.
	CodeInvalidCode Code = iota + 0x00_00_00_01 // 0.0.0.1

	// CodeInvalidReason identifies a Define call whose Reason is empty or
	// does not match the SCREAMING_SNAKE convention.
	CodeInvalidReason // 0.0.0.2

	// CodeInvalidPublic identifies a Define call whose Public message is
	// empty, exceeds 120 runes, or contains a newline.
	CodeInvalidPublic // 0.0.0.3

	// CodeInvalidPrivate identifies a Define call whose Private message is
	// empty. Introduced by ADR 0005 to close the validate.go bug where
	// CodeInvalidPublic was erroneously cited for Private-field failures.
	CodeInvalidPrivate // 0.0.0.4

	// CodeInvalidCodeString identifies a ParseCode failure (malformed
	// canonical dotted-quad input).
	CodeInvalidCodeString // 0.0.0.5

	// CodeInvalidWrapParams identifies a Wrap-time validation failure on
	// caller-supplied WrapParams. Wrap returns an *Error carrying this
	// code instead of panicking (runtime path, not init-time).
	CodeInvalidWrapParams // 0.0.0.6
)

func CodeOf

func CodeOf(err error) (c Code, ok bool)

CodeOf walks the chain and returns the deepest *Error's typed Code.

func Pack

func Pack(mm Major, ll Layer, pp PkgCode, ss Serial) Code

Pack constructs a Code from its four octets. RUNTIME only — sentinel constants MUST use hex literals so they stay const-expressible.

func ParseCode

func ParseCode(s string) (c Code, err error)

ParseCode parses the canonical "M.L.P.S" form produced by Code.String().

Rules (any violation returns an *Error with CodeInvalidCodeString):

  • Exactly 4 dot-separated segments.
  • Each segment is 1 to 3 ASCII digits.
  • No leading zero in any segment ("001", "01", "0001" → REJECTED). This excludes the Padded() form ("001.001.001.001") as input, so the canonical form is the only textual key that maps to a Code.
  • Each segment's numeric value is in [0, 255].
  • No leading/trailing whitespace, no sign character, no empty segment.

func TrailOf

func TrailOf(err error) []Code

TrailOf returns the wrap-trail codes of the deepest *Error in err's chain, newest wrap last, or nil when err carries no *Error.

The trail mirrors the wrap-site chain accumulated as the error was wrapped (the origin's own Code is NOT included — it is reached via CodeOf).

func (Code) Layer

func (c Code) Layer() Layer

Layer returns the second-highest octet.

func (Code) Major

func (c Code) Major() Major

Major returns the top octet (SemVer major byte).

func (Code) Package

func (c Code) Package() PkgCode

Package returns the third octet.

func (Code) Padded

func (c Code) Padded() string

Padded returns the zero-padded "MMM.LLL.PPP.SSS" form. DISPLAY ONLY — must never be used as a lookup key; the canonical form is String().

func (Code) Serial

func (c Code) Serial() Serial

Serial returns the bottom octet.

func (Code) String

func (c Code) String() string

String returns the CANONICAL dotted form "M.L.P.S" (unpadded). This form is used for storage, logs, fixtures, and lookup keys.

type DefineOption

type DefineOption func(e *Error)

DefineOption tunes an Error at Define time. Options compose left-to-right.

func WithExitCode

func WithExitCode(code int) DefineOption

WithExitCode overrides the default POSIX exit code (70) for an Error.

func WithHTTPStatus

func WithHTTPStatus(status int) DefineOption

WithHTTPStatus overrides the default HTTP status (500) for an Error.

type Error

type Error struct {
	// contains filtered or unexported fields
}

Error is the SDK-wide typed error. Fields are unexported — consumers obtain values via the getter methods on *Error or via the Of-accessors in github.com/kitsunium/sdk/pkg/v1/errs. Instances are immutable after construction: Wrap returns a new *Error, never mutates the input.

func Define

func Define(code Code, reason, public, private string, opts ...DefineOption) *Error

Define registers a sentinel-style *Error at package init. It panics (with a message citing one of the documentary meta-codes 0.0.0.1..6) when validateDefineArgs returns non-nil, so structural mistakes surface at program start rather than at the first emission.

func NewError

func NewError(code Code, reason, public, private string, opts ...DefineOption) *Error

NewError is a tooling-friendly alias for Define. Prefer Define at call sites for consistency; NewError exists so code generators and lints expecting a NewXxx constructor on exported types find one.

func NewRuntime added in v0.1.6

func NewRuntime(code Code, reason, public, private string, fields ...FieldValue) *Error

NewRuntime constructs an *Error at RUNTIME without panicking. It is the counterpart to Define for callers that get no build-time AST audit — principally external SDK consumers reaching this through pkg/v1/errs.New. Where Define panics at package init on a malformed sentinel (caught by the registry AST audit before the binary ships), NewRuntime mirrors Wrap's runtime policy: a structural failure returns a typed validation *Error (CodeInvalidCode / Reason / Public / Private) preserving the specific rule that failed, so the result is always a usable, introspectable SDK error and never nil. Fields are defensively copied. HTTP/exit overrides default (500 / 70); use Wrap with WrapParams.ExitCode or a sibling Define sentinel when an override is needed.

func Wrap

func Wrap(cause error, params WrapParams, fields ...FieldValue) *Error

Wrap attaches a cause to a new *Error with origin-wins semantics. Behaviour contract (see ADR 0005 §3.6): 1. cause == nil (interface nil) → stdlib path, source = nil 2. cause == (*Error)(nil) typed-nil → stdlib path, source = nil 3. cause == (*T)(nil) typed-nil of non-*Error → stdlib path, source preserved 4. cause is *Error (directly or behind a stdlib wrapper) → origin-wins, params.Code is APPENDED TO THE TRAIL (trail entry 0 is the origin) 5. cause is other stdlib error → params.Code is origin, trail stays empty

Runtime policy (v5 HIGH fix): bad WrapParams at runtime does NOT panic; the returned *Error carries CodeInvalidWrapParams and preserves cause.

func (*Error) Code

func (e *Error) Code() Code

Code returns this Error's typed dotted-quad identifier.

func (*Error) Error

func (e *Error) Error() string

Error returns the neutral stable form "[<code> <REASON>] <public>". It never includes Private content nor Fields, so callers may safely bubble err.Error() across any boundary without leaking diagnostic data.

When the trail is non-empty, wrap-site codes are rendered between the origin code and the Reason with ASCII "<-" separators, e.g.:

[0.3.2.1 <- 1.2.0.3 JSON_MARSHAL_FAILED] invalid input

Truncated trails append " (truncated)" before the Reason marker.

func (*Error) ExitCode

func (e *Error) ExitCode() int

ExitCode returns the per-error override if set, otherwise 70 EX_SOFTWARE.

func (*Error) Fields

func (e *Error) Fields() []FieldValue

Fields returns a defensive copy of the structured metadata attached to this Error. Mutating the returned slice has no effect on the Error.

func (*Error) HTTPStatus

func (e *Error) HTTPStatus() int

HTTPStatus returns the per-error override if set, otherwise 500.

func (*Error) Is

func (e *Error) Is(target error) bool

Is implements the errors.Is protocol. Three matching modes:

1. target is *PrefixMatcher → match if origin code OR any trail entry satisfies the CIDR-style mask (ADR 0005 prefix routing). 2. target is *Error with non-zero Code → match when the two share the same (Code, Reason). This lets errors.Is(err, sentinel) succeed even when err is a freshly-constructed Wrap result — the typed sentinel and the wire sentinel have identical Code+Reason by construction, and semantic equivalence is what callers expect. The Reason check defuses a hypothetical Code-collision (the registry audit is the primary guard; this is belt-and-suspenders). 3. anything else → pointer equality (stdlib default).

func (*Error) Private

func (e *Error) Private() string

Private returns this Error's log-only detailed message. Never expose the return value over any user-facing surface.

func (*Error) Public

func (e *Error) Public() string

Public returns this Error's wire-safe message.

func (*Error) Reason

func (e *Error) Reason() string

Reason returns this Error's stable SCREAMING_SNAKE identifier.

func (*Error) Source

func (e *Error) Source() error

Source returns the wrapped cause attached by Wrap.

func (*Error) Trail

func (e *Error) Trail() []Code

Trail returns a defensive copy of the wrap-site chain (newest last). The origin's code is NOT in the trail — it lives in Code().

func (*Error) TrailTruncated

func (e *Error) TrailTruncated() bool

TrailTruncated reports whether the trail's middle links have been dropped due to exceeding maxTrailLen. The flag is MONOTONIC: once set by appendTrail, it propagates unchanged to every subsequent wrap.

func (*Error) Unwrap

func (e *Error) Unwrap() error

Unwrap delegates to Source so the stdlib errors.Is / errors.As machinery can walk the chain. The nil-guard also makes this slightly more than a pure field getter, which matters for callers storing errors by value.

type FieldValue

type FieldValue struct {
	// contains filtered or unexported fields
}

FieldValue is an immutable typed key/value pair attached to an *Error. Use the exported constructors (String, Int, Bool, Float, NewFieldValue) to create a valid FieldValue — the zero value is invalid and must never be passed across the API.

func Bool

func Bool(key string, val bool) FieldValue

Bool builds a FieldValue holding a boolean value.

func FieldsOf

func FieldsOf(err error) []FieldValue

FieldsOf collects the FieldValues attached to every *Error along the chain and returns them ordered inner-to-outer (oldest cause first, newest wrapper last). The returned slice is a defensive copy.

func Float

func Float(key string, val float64) FieldValue

Float builds a FieldValue holding a float64 value.

func Int

func Int(key string, val int) FieldValue

Int builds a FieldValue from a plain int. Matches the slog / zap convention (both expose Int(key, int) that widens internally) so callers do not write int64(…) at every site. For pre-widened int64 payloads use Int64.

func Int64

func Int64(key string, val int64) FieldValue

Int64 builds a FieldValue from a 64-bit integer. Provided explicitly because Go does not support function overloading — callers that hold an int64 already (no upcast needed) reach for this constructor, while the common int case stays on Int.

func NewFieldValue

func NewFieldValue(key, val string) FieldValue

NewFieldValue builds a string-typed FieldValue. Provided as a generic constructor for tooling that expects a New-prefixed factory; for common cases prefer the dedicated String/Int/Bool/Float helpers.

func String

func String(key, val string) FieldValue

String builds a FieldValue holding a string value.

func (FieldValue) Key

func (f FieldValue) Key() string

Key returns the identifier under which this FieldValue was recorded.

func (FieldValue) StringValue

func (f FieldValue) StringValue() string

StringValue returns a deterministic textual rendering of the payload suitable for log lines and error dumps. Not guaranteed to be machine- parseable back into the original type — by design the consumer contract stops at textual observation.

type Layer

type Layer uint8

Named octet types prevent positional-argument footguns in Pack. PkgCode is spelled out (not `Pkg`) so callers keep `pkg` as an ordinary variable name without shadowing.

type Major

type Major uint8

Named octet types prevent positional-argument footguns in Pack. PkgCode is spelled out (not `Pkg`) so callers keep `pkg` as an ordinary variable name without shadowing.

type PkgCode

type PkgCode uint8

Named octet types prevent positional-argument footguns in Pack. PkgCode is spelled out (not `Pkg`) so callers keep `pkg` as an ordinary variable name without shadowing.

type PrefixMatcher

type PrefixMatcher struct {
	// contains filtered or unexported fields
}

PrefixMatcher is a NON-ERROR sentinel used ONLY as a target for errors.Is. Callers build it via NewPrefixMatcher and pass it to errors.Is; the matching logic lives on (*Error).Is (see error.go, added in W4).

func NewPrefixMatcher

func NewPrefixMatcher(prefix, mask Code) *PrefixMatcher

NewPrefixMatcher constructs a PrefixMatcher usable with errors.Is to match all Codes sharing a given bit-prefix (defined by the mask). Example: all codes originating from pkg/v1/* →

errors.Is(err, errs.NewPrefixMatcher(0x01_00_00_00, errs.MaskByMajor))

func (*PrefixMatcher) Error

func (p *PrefixMatcher) Error() string

Error implements the `error` interface so PrefixMatcher is usable as a target for `errors.Is`. Returning an error-looking string is the ONLY way Go's errors.Is can accept a non-sentinel target; callers MUST never return a *PrefixMatcher from a function as an error. A follow-up linter rule will catch accidental escapes.

func (*PrefixMatcher) Mask

func (p *PrefixMatcher) Mask() Code

Mask exposes the matcher's mask for internal callers.

func (*PrefixMatcher) Prefix

func (p *PrefixMatcher) Prefix() Code

Prefix exposes the matcher's prefix for internal callers (e.g., the (*Error).Is method) without leaking the field directly.

func (*PrefixMatcher) String

func (p *PrefixMatcher) String() string

String returns a diagnostic representation of the matcher.

type Serial

type Serial uint8

Named octet types prevent positional-argument footguns in Pack. PkgCode is spelled out (not `Pkg`) so callers keep `pkg` as an ordinary variable name without shadowing.

type WrapParams

type WrapParams struct {
	// Code is the dotted-quad identifier assigned to the wrapping Error.
	Code Code
	// Reason is the SCREAMING_SNAKE stable identifier of the wrapping Error.
	Reason string
	// Public is the wire-safe message (string literal at source level).
	Public string
	// Private is the log-only detailed message.
	Private string
	// ExitCode optionally overrides the POSIX exit status of the wrapping
	// Error (default 70 EX_SOFTWARE); the zero value keeps the default. It
	// lets a stdlib cause be wrapped into an Error that carries the same
	// exit semantics as a sibling errs.Define sentinel — the origin-wins
	// path already inherits a cause Error's exit override, but the
	// stdlib-cause path has no Error to inherit from.
	ExitCode int
}

WrapParams groups the extra metadata Wrap needs when the cause is NOT already an *Error. Keeping them in a struct stays below the SDK's 5-parameter ceiling for Wrap and gives call sites named fields.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL