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 ¶
- func ExitCodeOf(err error) int
- func HTTPStatusOf(err error) int
- func HasCode(err error, c Code) bool
- func HasReason(err error, reason string) bool
- func PrivateOf(err error) string
- func PublicOf(err error) string
- func ReasonOf(err error) (reason string, ok bool)
- func RegistryMarker() string
- type Code
- type DefineOption
- type Error
- func Define(code Code, reason, public, private string, opts ...DefineOption) *Error
- func NewError(code Code, reason, public, private string, opts ...DefineOption) *Error
- func NewRuntime(code Code, reason, public, private string, fields ...FieldValue) *Error
- func Static(code Code, reason, public, private string, http, exit int) *Error
- func Wrap(cause error, params WrapParams, fields ...FieldValue) *Error
- func (e *Error) Code() Code
- func (e *Error) Error() string
- func (e *Error) ExitCode() int
- func (e *Error) Fields() []FieldValue
- func (e *Error) HTTPStatus() int
- func (e *Error) Is(target error) bool
- func (e *Error) Private() string
- func (e *Error) Public() string
- func (e *Error) Reason() string
- func (e *Error) Source() error
- func (e *Error) Trail() []Code
- func (e *Error) TrailTruncated() bool
- func (e *Error) Unwrap() error
- type FieldValue
- func Bool(key string, val bool) FieldValue
- func FieldsOf(err error) []FieldValue
- func Float(key string, val float64) FieldValue
- func Int(key string, val int) FieldValue
- func Int64(key string, val int64) FieldValue
- func NewFieldValue(key, val string) FieldValue
- func String(key, val string) FieldValue
- type Layer
- type Major
- type PkgCode
- type PrefixMatcher
- type Serial
- type WrapParams
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func ExitCodeOf ¶
ExitCodeOf walks the chain and returns the deepest *Error.ExitCode().
func HTTPStatusOf ¶
HTTPStatusOf walks the chain and returns the deepest *Error.HTTPStatus().
func HasCode ¶
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 PrivateOf ¶
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 ¶
PublicOf walks the Unwrap chain and returns the deepest *Error.Public, or "" when no *Error is present in the chain.
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 Pack ¶
Pack constructs a Code from its four octets. RUNTIME only — sentinel constants MUST use hex literals so they stay const-expressible.
func ParseCode ¶
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 ¶
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).
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 ¶
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 Static ¶ added in v0.10.0
Static builds a sentinel-style *Error from constants alone, so the linker can initialise it: the body is one composite literal, with no validation, no panic, no variadic and no closure, and the call inlines. It is Define's sibling (ADR 0039), not a replacement: Define keeps validating at package init and keeps its options. Static does not validate — the design does (kit gen checks the reason's pattern, the public text, the code's layer and the status ranges, ADR 0176) and the registry audits read Static calls as they read Define calls. A zero http or exit means the default (500 / 70).
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) Error ¶
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) 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 ¶
HTTPStatus returns the per-error override if set, otherwise 500.
func (*Error) Is ¶
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 ¶
Private returns this Error's log-only detailed message. Never expose the return value over any user-facing surface.
func (*Error) Trail ¶
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 ¶
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.
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.