Documentation
¶
Overview ¶
Package errorfamily provides structured error classification and handling for Go. Every error gets a behavioral Family (retry/no-retry), a machine-readable code, human-readable context, and optional diagnostics. Designed for CLI and HTTP boundaries.
The contract is the interfaces, not the struct ¶
The public protocol is four small interfaces in interfaces.go: Coded, Classified, Contextual, and Retryable. Each error type implements the combination that makes sense for it; consumers ask for exactly what they need via errors.AsType[T].
The concrete Error type is a reference implementation — convenient for simple needs, but not required. Projects with domain-specific error shapes (a finding with File/Line, an HTTP error with Status) build their own struct and implement only the interfaces they need. Share the protocol, not the implementation.
Stability ¶
The root package (classification core) is the stable foundation. The agent, diagnose, and bridge packages are experimental (v0.x).
Example (ConditionalRequests) ¶
Conditional requests (RFC 9110 §13): families are behavioral, statuses are wire-level — WithHTTPStatus is the seam. A 304 is a success outcome, never a classified error: write it and return nil.
// 412 Precondition Failed: the client's If-Match ETag no longer matches
// the current state — a version mismatch, so Conflict.
err := NewConflict("etag.precondition_failed", "If-Match precondition failed").
WithHTTPStatus(412)
fmt.Println(HTTPStatus(err), Classify(err), IsRetryable(err), ExitCode(err))
// 428 Precondition Required (RFC 6585 §3): the request omitted a required
// precondition — incomplete input, so Rejection.
err = NewRejection("etag.precondition_required", "conditional request required").
WithHTTPStatus(428)
fmt.Println(HTTPStatus(err), Classify(err), IsRetryable(err), ExitCode(err))
Output: 412 conflict false 1 428 rejection false 1
Index ¶
- Variables
- func Code(err error) string
- func Compose(errs ...error) error
- func ExitCode(err error) int
- func HTTPHandler(h HandlerFunc) http.Handler
- func HTTPStatus(err error) int
- func HandleError(err error) int
- func HandleErrorWithConfig(err error, cfg HandleConfig) int
- func HandleErrorWithContext(ctx context.Context, err error, cfg HandleConfig) int
- func IsRetryable(err error) bool
- func LogError(err error, logger *slog.Logger)
- func LogErrorContext(ctx context.Context, err error, logger *slog.Logger)
- func RegisterClassification(sentinel error, family Family)
- func RegisterClassificationType[T error](family Family)
- func RegisterClassificationTypeFor[T error](r *Registry, family Family)
- func RegisterClassifications(classifications map[error]Family)
- func RegisterClassifier(c Classifier)
- func RegisterClassifiers(cs ...Classifier)
- func RegisterStdlibDefaults(reg *Registry)
- func RegisterTemplate(code string, tmpl MessageTemplate)
- func UnregisterClassification(sentinel error)
- func UnregisterTemplate(code string)
- type Audience
- type Classified
- type Classifier
- type Coded
- type Contextual
- type DiagnosticFinding
- type DiagnosticFunc
- type Error
- func New(family Family, code, message string) *Error
- func NewConflict(code, message string) *Error
- func NewCorruption(code, message string) *Error
- func NewInfrastructure(code, message string) *Error
- func NewOrchestration(code, message string) *Error
- func NewRejection(code, message string) *Error
- func NewTransient(code, message string) *Error
- func Newf(family Family, code, format string, args ...any) *Error
- func Wrap(err error, family Family, code, message string) *Error
- func WrapConflict(err error, code, message string) *Error
- func WrapConflictf(err error, code, format string, args ...any) *Error
- func WrapCorruption(err error, code, message string) *Error
- func WrapCorruptionf(err error, code, format string, args ...any) *Error
- func WrapInfrastructure(err error, code, message string) *Error
- func WrapInfrastructuref(err error, code, format string, args ...any) *Error
- func WrapOnce(err error, family Family, code, message string) *Error
- func WrapOncef(err error, family Family, code, format string, args ...any) *Error
- func WrapOrchestration(err error, code, message string) *Error
- func WrapOrchestrationf(err error, code, format string, args ...any) *Error
- func WrapRejection(err error, code, message string) *Error
- func WrapRejectionf(err error, code, format string, args ...any) *Error
- func WrapTransient(err error, code, message string) *Error
- func WrapTransientf(err error, code, format string, args ...any) *Error
- func Wrapf(err error, family Family, code, format string, args ...any) *Error
- func (e *Error) Cause() error
- func (e *Error) Code() string
- func (e *Error) ContextValue(key string) string
- func (e *Error) Error() string
- func (e *Error) ErrorCode() string
- func (e *Error) ErrorContext() map[string]string
- func (e *Error) ErrorFamily() Family
- func (e *Error) ExitCode() int
- func (e *Error) Family() Family
- func (e *Error) Format(f fmt.State, verb rune)
- func (e *Error) HTTPStatus() int
- func (e *Error) HasContext(key string) bool
- func (e *Error) Is(target error) bool
- func (e *Error) IsRetryable() bool
- func (e *Error) JSON() ([]byte, error)
- func (e *Error) Message() string
- func (e *Error) Summary() string
- func (e *Error) Timestamp() time.Time
- func (e *Error) Unwrap() error
- func (e *Error) WithCause(cause error) *Error
- func (e *Error) WithContext(key, value string) *Error
- func (e *Error) WithContextAny(key string, value any) *Error
- func (e *Error) WithContextMap(ctx map[string]string) *Error
- func (e *Error) WithContextf(key, format string, args ...any) *Error
- func (e *Error) WithExitCode(code int) *Error
- func (e *Error) WithHTTPStatus(status int) *Error
- func (e *Error) WithTimestamp(ts time.Time) *Error
- type ExitCoder
- type Family
- func (f Family) Audience() Audience
- func (f Family) DefaultFix() string
- func (f Family) DefaultMessage() string
- func (f Family) DefaultWhy() string
- func (f Family) ExitCode() int
- func (f Family) HTTPStatus() int
- func (f Family) IsRetryable() bool
- func (f Family) IsValid() bool
- func (f Family) MarshalText() ([]byte, error)
- func (f Family) RetryPolicy() RetryPolicy
- func (f Family) Severity() int
- func (f Family) String() string
- func (f Family) Tone() Tone
- func (f *Family) UnmarshalText(text []byte) error
- type HTTPStatuser
- type HandleConfig
- type HandleResult
- type HandlerFunc
- type MessageTemplate
- type Registry
- func (r *Registry) Classify(err error) Family
- func (r *Registry) Clone() *Registry
- func (r *Registry) RegisterClassification(sentinel error, family Family)
- func (r *Registry) RegisterClassifications(classifications map[error]Family)
- func (r *Registry) RegisterClassifier(c Classifier)
- func (r *Registry) RegisterClassifiers(classifiers ...Classifier)
- func (r *Registry) RegisterTemplate(code string, tmpl MessageTemplate)
- func (r *Registry) RegisterTemplates(templates map[string]MessageTemplate)
- func (r *Registry) TemplateForCode(code string) (MessageTemplate, bool)
- func (r *Registry) UnregisterClassification(sentinel error)
- func (r *Registry) UnregisterTemplate(code string)
- type RetryPolicy
- type Retryable
- type Tone
Examples ¶
- Package (ConditionalRequests)
- Classify
- Error.JSON
- Error.WithContextAny
- Error.WithExitCode
- Error.WithHTTPStatus
- ExitCode
- Family.HTTPStatus
- Family.MarshalText
- Family.RetryPolicy
- Family.UnmarshalText
- HTTPHandler
- HTTPStatus
- HandleError
- HandleErrorDetailed
- HandleErrorWithContext
- IsRetryable
- LogError
- NewConflict
- NewCorruption
- NewInfrastructure
- NewOrchestration
- NewRegistry
- NewTransient
- ParseFamily
- RegisterClassification
- RegisterClassificationType
- Wrap
- WrapOnce
- WrapRejection
Constants ¶
This section is empty.
Variables ¶
var DefaultRegistry = NewRegistry()
DefaultRegistry is the package-level Registry used by the convenience functions (Classify, RegisterClassification, RegisterTemplate, etc.) and by HandleError when HandleConfig.Registry is nil.
Functions ¶
func Code ¶ added in v0.6.0
Code extracts the machine-readable error code from any error in the chain. It walks the unwrap chain via errors.AsType looking for the Coded interface. Returns "" if the error (or any wrapped cause) does not implement Coded.
This is the one-liner replacement for:
var coded errorfamily.Coded
if errors.As(err, &coded) {
code = coded.ErrorCode()
}
func Compose ¶ added in v0.3.0
Compose joins multiple errors into one, preserving all in the Unwrap chain. It is a thin wrapper around errors.Join kept for backward compatibility with consumers that imported Compose before v0.5.0 reorganized the package.
func ExitCode ¶
ExitCode returns the appropriate process exit code for an error. Nil errors return 0.
If the error (or any error in its chain) implements ExitCoder with a non-zero code, that code is returned — allowing individual errors to override their family's canonical exit code. Otherwise, the error is classified and mapped to BSD sysexits.h codes via Family.ExitCode.
Example ¶
// ExitCode checks the ExitCoder interface first (per-error override),
// then falls back to the family's canonical exit code.
fmt.Println(ExitCode(NewTransient("db.timeout", "msg")))
fmt.Println(ExitCode(NewRejection("bad.input", "msg")))
fmt.Println(ExitCode(NewTransient("custom", "msg").WithExitCode(5)))
Output: 75 1 5
func HTTPHandler ¶ added in v0.6.0
func HTTPHandler(h HandlerFunc) http.Handler
HTTPHandler wraps a HandlerFunc, classifying any returned error and writing a JSON error response with the status code from Family.HTTPStatus.
mux.Handle("/api/orders", errorfamily.HTTPHandler(createOrder))
The response body is intentionally safe — it never leaks the raw error message. It contains:
- "family": the behavioral family (e.g. "rejection")
- "code": the machine-readable code (omitted when empty)
- "message": a user-facing message from a registered MessageTemplate (omitted when no template is registered for the code)
For a custom response shape, write your own response inside the handler on the success path and use HTTPStatus directly for the failure path.
Example ¶
// HTTPHandler bridges classification into net/http: any error returned
// by the handler is classified and written as a safe JSON response.
// The body never contains the raw error message.
fetch := HTTPHandler(func(w http.ResponseWriter, r *http.Request) error {
return NewRejection("battle.not_found", "battle 42 missing").WithHTTPStatus(404)
})
rec := httptest.NewRecorder()
fetch.ServeHTTP(
rec,
httptest.NewRequestWithContext(context.Background(), http.MethodGet, "/battles/42", nil),
)
fmt.Println(rec.Code)
fmt.Println(rec.Body.String())
Output: 404 {"code":"battle.not_found","family":"rejection"}
func HTTPStatus ¶ added in v0.6.0
HTTPStatus returns the recommended HTTP response status code for an error. If the error (or any error in its chain) implements HTTPStatuser with a non-zero status, that status overrides the family-based default.
w.WriteHeader(errorfamily.HTTPStatus(err))
Nil errors classify as Rejection → 400; prefer checking for nil before reaching the HTTP layer.
Example ¶
// HTTPStatus checks the HTTPStatuser interface first (per-error override),
// then falls back to the family's canonical HTTP status.
fmt.Println(HTTPStatus(NewRejection("bad.input", "msg")))
fmt.Println(HTTPStatus(NewConflict("order.duplicate", "msg")))
fmt.Println(HTTPStatus(NewRejection("not.found", "msg").WithHTTPStatus(404)))
Output: 400 409 404
func HandleError ¶
HandleError is the CLI boundary handler — the meta service.
Call this exactly once at the top of main():
func main() {
if err := run(); err != nil {
os.Exit(errorfamily.HandleError(err))
}
}
It:
- Classifies the error (Family → exit code)
- Extracts Code and Context for specific messaging
- Optionally runs diagnostic rules
- Formats a Wix-quality message for the user
- Writes to stderr and returns the exit code
Example ¶
err := NewRejection("file.not_found", "config missing").
WithContext("path", "/etc/app/config.yaml")
_ = HandleError(err)
func HandleErrorWithConfig ¶
func HandleErrorWithConfig(err error, cfg HandleConfig) int
HandleErrorWithConfig is the configurable version of HandleError.
func HandleErrorWithContext ¶ added in v0.3.0
func HandleErrorWithContext(ctx context.Context, err error, cfg HandleConfig) int
HandleErrorWithContext handles an error with caller-provided context for cancellation and diagnostic propagation. This is the preferred entry point when the caller has a context.Context available.
All other HandleError variants delegate to this function.
Example ¶
// HandleErrorWithContext is the canonical entry point — HandleError and
// HandleErrorWithConfig delegate to it. Prefer it whenever a
// context.Context is available.
err := NewRejection("file.not_found", "config missing").
WithContext("path", "/etc/app/config.yaml")
code := HandleErrorWithContext(context.Background(), err, HandleConfig{
Output: io.Discard,
})
fmt.Println(code)
Output: 1
func IsRetryable ¶
IsRetryable reports whether the error is worth retrying. Uses Classify() and checks if the result is Transient.
Example ¶
fmt.Println(IsRetryable(NewTransient("db.timeout", "query timed out")))
fmt.Println(IsRetryable(NewRejection("bad.input", "invalid input")))
fmt.Println(IsRetryable(errors.New("unknown origin")))
Output: true false true
func LogError ¶ added in v0.6.0
LogError logs an error with structured fields derived from its classification: family, code, retryable, exit_code, and every error-context key (prefixed with "context.").
Severity mapping follows the behavioral family:
- Transient (retryable) → slog.LevelWarn, since these are expected to self-heal.
- All other families → slog.LevelError.
A nil error is a no-op. Pass a nil logger to use slog.Default.
logger := slog.Default()
if err := run(); err != nil {
errorfamily.LogError(err, logger)
}
Example ¶
// LogError maps the behavioral family onto slog severity: Transient
// (expected to self-heal) logs at Warn, everything else at Error.
logger := slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{
ReplaceAttr: func(_ []string, a slog.Attr) slog.Attr {
if a.Key == slog.TimeKey {
return slog.Attr{}
}
return a
},
}))
err := NewTransient("db.timeout", "query timed out").
WithContext("host", "db1")
LogError(err, logger)
Output: level=WARN msg="[transient:db.timeout] query timed out" family=transient code=db.timeout retryable=true exit_code=75 context.host=db1
func LogErrorContext ¶ added in v0.6.0
LogErrorContext is the context-accepting variant of LogError, for cancellation propagation and trace correlation in instrumented services.
func RegisterClassification ¶
RegisterClassification maps a third-party sentinel error to a Family. Thread-safe. Call from init() in external packages:
func init() {
errorfamily.RegisterClassification(sql.ErrConnDone, errorfamily.Transient)
}
This is for errors you don't own (stdlib, libraries). For your own errors, implement the Classified interface instead.
Delegates to DefaultRegistry. For scoped registration, use Registry.RegisterClassification on a custom Registry.
Example ¶
sentinel := errors.New("connection pool exhausted")
RegisterClassification(sentinel, Transient)
family := Classify(sentinel)
fmt.Println(family)
Output: transient
func RegisterClassificationType ¶ added in v0.8.0
RegisterClassificationType registers a type-based classifier that maps any error matching type T to the given Family. It is syntactic sugar over RegisterClassifier for the common case where the classification rule is simply "type T → Family F", without field-level inspection.
errorfamily.RegisterClassificationType[*sqlite.Error](errorfamily.Transient)
For errors that need field-level logic (e.g. different sqlite error codes mapping to different families), use RegisterClassifier with a closure.
For a custom Registry, use RegisterClassificationTypeFor.
Delegates to DefaultRegistry.
Example ¶
// RegisterClassificationType maps any error of type T to a Family.
// Useful for dynamic third-party errors where every instance is fresh.
reg := NewRegistry()
RegisterClassificationTypeFor[*exampleSQLError](reg, Transient)
err := &exampleSQLError{msg: "database is locked"}
fmt.Println(reg.Classify(err))
Output: transient
func RegisterClassificationTypeFor ¶ added in v0.8.0
RegisterClassificationTypeFor registers a type-based classifier on a specific Registry. It is the generic counterpart to Registry.RegisterClassifier:
errorfamily.RegisterClassificationTypeFor[*sqlite.Error](myRegistry, errorfamily.Transient)
Go does not permit type parameters on methods, so this is a top-level function rather than a method on Registry.
func RegisterClassifications ¶
RegisterClassifications registers multiple sentinel-to-Family mappings at once. Thread-safe. Call from init() in external packages.
func RegisterClassifier ¶ added in v0.6.0
func RegisterClassifier(c Classifier)
RegisterClassifier adds a predicate-based Classifier for dynamic third-party errors that cannot be registered as sentinels (e.g. *sqlite.Error). Thread-safe. Classifiers run after sentinel matching fails, in registration order; the first returning ok=true wins.
Because Go func values are not comparable, individual classifiers cannot be unregistered. For test isolation or scoped handling, construct a NewRegistry and call Registry.RegisterClassifier instead of polluting DefaultRegistry.
Delegates to DefaultRegistry.
func RegisterClassifiers ¶ added in v0.6.0
func RegisterClassifiers(cs ...Classifier)
RegisterClassifiers adds multiple predicate-based Classifier funcs at once. Thread-safe. Classifiers run in registration order. See RegisterClassifier.
Delegates to DefaultRegistry.
func RegisterStdlibDefaults ¶ added in v0.5.0
func RegisterStdlibDefaults(reg *Registry)
RegisterStdlibDefaults registers classification mappings for common Go standard-library errors onto the given Registry. Pass DefaultRegistry to affect the package-level Classify, or a custom Registry for scoped handling.
Ambiguous cases and the rationale for their assigned Family:
- context.DeadlineExceeded → Transient (retryable). Usually means an upstream was slow; a fresh attempt may succeed. If the deadline was set too short by the caller, the fix is at the call site, but the error itself is still transient in nature.
- context.Canceled → Rejection (not retryable). The caller abandoned the operation; retrying without a new request is wrong.
- sql.ErrNoRows / os.ErrNotExist → Rejection. A missing resource is the caller's concern (wrong key / path), not a system fault.
- sql.ErrConnDone → Transient. The connection pool closed a connection; a retry on a fresh connection typically succeeds.
- os.ErrPermission → Rejection. Permission is a caller/environment state, not a transient failure.
This is advisory: applications whose semantics differ should register their own mappings after calling this.
func RegisterTemplate ¶
func RegisterTemplate(code string, tmpl MessageTemplate)
RegisterTemplate adds a MessageTemplate for a specific error code. Thread-safe. Overrides any existing template for the same code.
Delegates to DefaultRegistry. For scoped registration, use Registry.RegisterTemplate on a custom Registry.
func UnregisterClassification ¶ added in v0.3.0
func UnregisterClassification(sentinel error)
UnregisterClassification removes a previously registered sentinel mapping. Thread-safe. No-op if the sentinel has no registered classification.
func UnregisterTemplate ¶ added in v0.3.0
func UnregisterTemplate(code string)
UnregisterTemplate removes a previously registered template. Thread-safe. No-op if the code has no registered template.
Types ¶
type Audience ¶
type Audience int
Audience describes who should be notified about this error.
func ParseAudience ¶ added in v0.4.0
ParseAudience parses an audience string, case-insensitive. Returns AudienceUser for unrecognized values (safest default).
func (Audience) IsValid ¶ added in v0.4.0
IsValid reports whether the Audience value is one of the three defined constants.
func (Audience) MarshalText ¶ added in v0.4.0
MarshalText implements encoding.TextMarshaler for YAML/JSON config.
func (Audience) String ¶ added in v0.1.2
String returns the lowercase name of the Audience (e.g. "user", "ops").
func (*Audience) UnmarshalText ¶ added in v0.4.0
UnmarshalText implements encoding.TextUnmarshaler for YAML/JSON config. Unknown values are parsed as AudienceUser (safest default).
type Classified ¶
Classified provides behavioral classification. Required for: retry decisions, exit codes, circuit breakers, tone selection.
type Classifier ¶ added in v0.6.0
Classifier is a predicate-based error classifier for dynamic third-party errors that cannot be registered as sentinels — each error is a fresh instance not matchable by errors.Is identity (e.g. *sqlite.Error, *pgconn.PgError).
Register one via RegisterClassifier or Registry.RegisterClassifier. During Classify, after the Classified interface, Retryable inference, and registered sentinels all miss, each registered Classifier runs in registration order; the first to return ok=true wins. Classifiers only run on otherwise-unclassified errors, so the hot path (errors that already declare a family) is untouched.
Example — classifying modernc.org/sqlite dynamic errors:
errorfamily.RegisterClassifier(func(err error) (errorfamily.Family, bool) {
var sqliteErr *sqlite.Error
if errors.As(err, &sqliteErr) {
switch sqliteErr.Code() {
case 5, 6: return errorfamily.Transient, true // BUSY, LOCKED
case 19: return errorfamily.Conflict, true // CONSTRAINT
}
}
return errorfamily.Transient, false
})
type Coded ¶
Coded provides machine-readable identity. Required for: metric labels, log fields, message templates, exit code mapping.
type Contextual ¶
Contextual provides factual details about the error. Required for: structured logging, specific error messages, debug context, and triggering diagnostic rules.
type DiagnosticFinding ¶
type DiagnosticFinding struct {
// RuleName identifies which diagnostic rule produced this finding.
RuleName string
// Status is the check outcome: "healthy", "degraded", "failed", or "unknown".
Status string
// Summary is a human-readable explanation of the finding.
Summary string
// SuggestedFix is an actionable resolution suggested by the rule.
SuggestedFix string
// Confidence is 0.0–1.0 indicating how likely this explains the error.
Confidence float64
}
DiagnosticFinding is a minimal result type for the CLI boundary. Avoids importing diagnose while preserving type safety.
type DiagnosticFunc ¶
type DiagnosticFunc func(ctx context.Context, err error) []DiagnosticFinding
DiagnosticFunc runs diagnostics for an error and returns results. The diagnose.Runner satisfies this via its Run method.
type Error ¶
type Error struct {
// contains filtered or unexported fields
}
Error is the reference implementation of a classified, structured error.
This is a convenience type, not the contract. The public protocol is the Coded/Classified/Contextual/Retryable interfaces — your own domain error types implement only those and need not embed or use this struct.
Projects with simple error needs can use this directly. Projects with domain-specific needs (e.g., FindingError with Position/File) can build their own struct and implement the Coded/Classified/Contextual interfaces.
Implements: error, Coded, Classified, Contextual, Retryable, ExitCoder, fmt.Formatter.
func NewConflict ¶
NewConflict creates a Conflict error (version mismatch, duplicate).
Example ¶
err := NewConflict("game.already_finished", "move after game over").
WithContext("game_id", "g-42")
fmt.Println(err, Classify(err).HTTPStatus(), Classify(err).IsRetryable())
Output: [conflict:game.already_finished] move after game over 409 false
func NewCorruption ¶
NewCorruption creates a Corruption error (data damaged, not self-healable).
Example ¶
err := NewCorruption("state.checksum_mismatch", "stored game state failed checksum")
fmt.Println(err, Classify(err).HTTPStatus(), Classify(err).Severity())
Output: [corruption:state.checksum_mismatch] stored game state failed checksum 500 6
func NewInfrastructure ¶
NewInfrastructure creates an Infrastructure error (system cannot serve).
Example ¶
err := NewInfrastructure("fs.readonly", "filesystem mounted read-only").
WithContext("path", "/var/lib/app")
fmt.Println(err, Classify(err).HTTPStatus(), Classify(err).IsRetryable())
Output: [infrastructure:fs.readonly] filesystem mounted read-only 503 false
func NewOrchestration ¶ added in v0.10.0
NewOrchestration creates an Orchestration error (internal coordination failure). Use this for bugs, misconfigurations, and internal logic failures that are neither I/O failures nor user input errors — e.g. "render status tree", "build CLI command", "wire dependencies". Not retryable. No user-facing fix.
Example ¶
err := NewOrchestration( "workflow.double_transition", "state machine applied the same transition twice", ) fmt.Println(err, Classify(err).HTTPStatus(), Classify(err).ExitCode())
Output: [orchestration:workflow.double_transition] state machine applied the same transition twice 500 70
func NewRejection ¶
NewRejection creates a Rejection error (bad input, unauthorized, not found).
func NewTransient ¶
NewTransient creates a Transient error (temporary failure, retryable).
Example ¶
err := NewTransient("db.timeout", "query took too long").
WithContext("host", "localhost").
WithContext("port", "5432")
fmt.Println(err)
Output: [transient:db.timeout] query took too long
func Wrap ¶
Wrap wraps an existing error with family, code, and message. Returns nil if err is nil.
Example ¶
// Wrap attaches a behavioral family to an error from a lower layer
// (stdlib, driver, another library) at the layer that knows the domain.
_, openErr := os.Open("/etc/app/config.yaml")
err := Wrap(openErr, Infrastructure, "config.unreadable", "cannot read config")
fmt.Println(err)
fmt.Println(Classify(err))
Output: [infrastructure:config.unreadable] cannot read config: open /etc/app/config.yaml: no such file or directory infrastructure
func WrapConflict ¶
WrapConflict wraps an error as Conflict.
func WrapConflictf ¶ added in v0.6.0
WrapConflictf wraps an error as Conflict with a formatted message.
func WrapCorruption ¶
WrapCorruption wraps an error as Corruption.
func WrapCorruptionf ¶ added in v0.6.0
WrapCorruptionf wraps an error as Corruption with a formatted message.
func WrapInfrastructure ¶
WrapInfrastructure wraps an error as Infrastructure.
func WrapInfrastructuref ¶ added in v0.6.0
WrapInfrastructuref wraps an error as Infrastructure with a formatted message.
func WrapOnce ¶ added in v0.8.0
WrapOnce wraps an error only if it is not already a *Error. Use this at API boundaries where errors may have been classified by an inner layer. Prevents double-wrapping chains like:
[transient:db.timeout] outer: [transient:db.timeout] inner: cause
If err is already a *Error, it is returned unchanged. If err is nil, returns nil (same nil-safety as Wrap).
Example ¶
// Inner layer already classified the error.
inner := NewTransient("db.timeout", "database timed out")
// API boundary: WrapOnce returns the existing *Error unchanged
// instead of creating a double-wrapped chain.
err := WrapOnce(inner, Infrastructure, "api.error", "API failed")
fmt.Println(err == inner)
Output: true
func WrapOncef ¶ added in v0.8.0
WrapOncef is the formatted-message variant of WrapOnce. Wraps an error only if it is not already a *Error, using a printf-style format string for the message. Returns nil if err is nil. Returns the existing *Error unchanged if already classified.
func WrapOrchestration ¶ added in v0.10.0
WrapOrchestration wraps an error as Orchestration (internal coordination failure).
func WrapOrchestrationf ¶ added in v0.10.0
WrapOrchestrationf wraps an error as Orchestration with a formatted message.
func WrapRejection ¶
WrapRejection wraps an error as Rejection.
Example ¶
orig := errors.New("file does not exist")
err := WrapRejection(orig, "config.invalid", "bad configuration")
fmt.Println(err)
Output: [rejection:config.invalid] bad configuration: file does not exist
func WrapRejectionf ¶ added in v0.6.0
WrapRejectionf wraps an error as Rejection with a formatted message.
func WrapTransient ¶
WrapTransient wraps an error as Transient (retryable).
func WrapTransientf ¶ added in v0.6.0
WrapTransientf wraps an error as Transient (retryable) with a formatted message.
func (*Error) Code ¶
Code returns the machine-readable error code.
This is a direct accessor — a convenience sibling of Error.Family and Error.Message for use when you already hold a concrete *Error and want the field without an interface assertion. It returns the SAME value as Error.ErrorCode; the two exist because ErrorCode is the Coded interface contract (used via errors.AsType / Code(err)), while Code is the ergonomic accessor on the concrete type. Neither is deprecated.
func (*Error) ContextValue ¶
ContextValue returns a specific context value, or empty string if not present.
func (*Error) Error ¶
Error implements the error interface. Returns a compact format: [family:code] message. The cause string is extracted via [safeCauseString] to guard against panics from misbehaving wrapped error types.
func (*Error) ErrorCode ¶
ErrorCode returns the machine-readable error code.
This is the canonical accessor: it satisfies the Coded interface, which is the public contract for code extraction (used by Code, HandleError, metrics, and log fields). Prefer this over Error.Code when you need the interface behavior. See Error.Code for the difference.
func (*Error) ErrorContext ¶
ErrorContext returns a copy of the error's factual context.
func (*Error) ErrorFamily ¶
ErrorFamily returns the behavioral classification.
func (*Error) ExitCode ¶ added in v0.8.0
ExitCode returns the custom process exit code if one was set via [WithExitCode]. Returns 0 when no custom code is set, signaling callers to fall back to the family-based default from Family.ExitCode.
This satisfies the ExitCoder interface so that ExitCode (the package-level function) can discover per-error overrides.
func (*Error) Family ¶
Family returns the error's behavioral classification. Convenience accessor for direct use without interface assertion.
func (*Error) Format ¶
Format implements fmt.Formatter.
%v → [family:code] message %+v → verbose multi-line with context and cause chain %s → message only (no classification prefix)
func (*Error) HTTPStatus ¶ added in v0.8.0
HTTPStatus returns the custom HTTP response status code if one was set via [WithHTTPStatus]. Returns 0 when no custom status is set, signaling callers to fall back to the family-based default from Family.HTTPStatus.
This satisfies the HTTPStatuser interface so that HTTPStatus (the package-level function) can discover per-error overrides.
func (*Error) HasContext ¶
HasContext reports whether the error has a specific context key.
func (*Error) Is ¶
Is supports errors.Is by matching error code and family. Two errors match if they have the same code AND family.
func (*Error) IsRetryable ¶
IsRetryable reports whether this error is worth retrying.
func (*Error) JSON ¶ added in v0.5.0
JSON returns a canonical JSON encoding of the error for API boundaries. The shape is stable: {family, code, message, context, retryable, timestamp}. Use this for HTTP/REST error responses where a structured body is preferable to the [transient:code] message format of Error().
Example ¶
err := NewTransient("db.timeout", "query timed out").
WithContext("host", "db1").
WithTimestamp(time.Date(2026, 1, 1, 0, 0, 0, 0, time.UTC))
data, _ := err.JSON()
fmt.Println(string(data))
Output: {"family":"transient","code":"db.timeout","message":"query timed out","context":{"host":"db1"},"retryable":true,"timestamp":"2026-01-01T00:00:00Z"}
func (*Error) Summary ¶
Summary returns a one-line human-readable summary suitable for logs and CLI output. Format: "code: message" without family prefix. The cause string is extracted via [safeCauseString] to guard against panics.
func (*Error) WithContext ¶
WithContext adds a key-value pair to the error's context. Returns a new Error, leaving the original unchanged — safe for shared/sentinel errors.
func (*Error) WithContextAny ¶ added in v0.8.0
WithContextAny adds a key-value pair to the error's context where the value can be of any type. The value is converted to string via a type switch for common types (string, int, int64, uint, uint64, float64, bool) and falls back to fmt.Sprint for everything else. Returns a new Error, leaving the original unchanged — safe for shared/sentinel errors.
Example ¶
// Add context values of any type — they are converted to strings.
err := NewRejection("validation.failed", "invalid input").
WithContextAny("count", 3).
WithContextAny("enabled", true)
fmt.Println(err.ContextValue("count"))
fmt.Println(err.ContextValue("enabled"))
Output: 3 true
func (*Error) WithContextMap ¶ added in v0.5.0
WithContextMap merges a map of key-value pairs into the error's context. Returns a new Error, leaving the original unchanged. Nil or empty input returns a clone with no added context.
func (*Error) WithContextf ¶ added in v0.5.0
WithContextf adds a formatted key-value pair to the error's context. The value is produced by fmt.Sprintf(format, args...). Returns a new Error, leaving the original unchanged.
func (*Error) WithExitCode ¶ added in v0.8.0
WithExitCode sets a custom process exit code that overrides the family-based default from Family.ExitCode. A value of 0 means "not set" — callers should fall back to the family's canonical exit code.
Note: os.Exit wraps the code to the range 0-255 on POSIX (e.g., -1 becomes 255). Use values in the 1-125 range for maximum portability (126+ have special meaning in some shells).
Returns a new Error, leaving the original unchanged — safe for shared/sentinel errors.
Example ¶
// Override the family-based exit code for a specific error.
// Useful when a transient error should exit with a code that
// differs from the BSD sysexits default (75).
err := NewTransient("rate.limited", "too many requests").WithExitCode(88)
fmt.Println(err.ExitCode())
Output: 88
func (*Error) WithHTTPStatus ¶ added in v0.8.0
WithHTTPStatus sets a custom HTTP response status code that overrides the family-based default from Family.HTTPStatus. A value of 0 means "not set" — callers should fall back to the family's canonical HTTP status.
Use this for errors whose family default is misleading at the HTTP layer:
notFound := NewRejection("battle.not_found", "battle not found").
WithHTTPStatus(http.StatusNotFound) // 404 instead of family default 400
Returns a new Error, leaving the original unchanged — safe for shared/sentinel errors.
Example ¶
// Override the family-based HTTP status for a specific error.
// The family default for Rejection is 400 (Bad Request), but a
// "not found" error is more precisely a 404.
err := NewRejection("battle.not_found", "battle not found").WithHTTPStatus(404)
fmt.Println(err.HTTPStatus())
Output: 404
type ExitCoder ¶ added in v0.8.0
ExitCoder errors carry a custom process exit code that overrides the family-based default from Family.ExitCode. When ExitCode encounters an error implementing this interface with a non-zero code, that code wins.
This allows individual errors to deviate from their family's canonical exit code without changing the family classification itself.
type Family ¶
type Family int
Family classifies an error's behavioral profile for automated handling.
One concept serving three audiences:
- Retry loops: "Should I try again?" (Transient = yes)
- Exit codes: "Which exit code for the shell?" (maps to BSD sysexits.h)
- Presentation: "Whose fault is it?" (determines tone and framing in user messages)
const ( // Rejection indicates bad input, unauthorized access, or resource not found. // No state changed. Not retryable. User's fault. Tone: helpful, instructional. Rejection Family = iota // Conflict indicates version mismatch, duplicate creation, or state machine violation. // No state changed for requester. Not retryable. User needs to resolve. Tone: explanatory. Conflict // Transient indicates a temporary infrastructure failure. // State unknown. Retryable with backoff. System's fault. Tone: reassuring. Transient // Corruption indicates the source of truth is damaged (unparseable payload, schema break). // Not self-healable. Not retryable. Serious. Tone: urgent, escalate to ops. Corruption // Infrastructure indicates the system cannot serve (closed, nil deps, startup failure). // Not retryable. System's fault. Tone: apologetic. Infrastructure // Orchestration indicates an internal coordination failure — the program's own // logic failed to complete an operation (rendering output, building a CLI command, // wiring dependencies, writing config). Not an I/O failure, not user input, not // a data-integrity break: a bug or misconfiguration in the program itself. // Not retryable. System's fault. No user-facing fix (report the bug). Orchestration )
func Classify ¶
Classify returns the Family of any error by checking multiple sources:
- Multi-error support (errors.Join) — first non-Transient wins
- Classified interface — the error itself declares its family
- Retryable interface — infer from retryability if no family
- Registered sentinels — known third-party errors mapped via RegisterClassification
- Registered classifiers — predicate-based matching for dynamic errors (RegisterClassifier)
- Default — Transient (fail-open for retry)
Returns Rejection for nil errors.
This function delegates to DefaultRegistry. For scoped or test-isolated classification, construct a Registry via NewRegistry and use its Classify method.
Example ¶
err := NewRejection("file.not_found", "config missing")
family := Classify(err)
fmt.Println(family.IsRetryable(), family.ExitCode())
Output: false 1
func ParseFamily ¶
ParseFamily parses a family string, case-insensitive. Returns Transient for unrecognized values (fail-open for retry).
Example ¶
fmt.Println(ParseFamily("transient"))
fmt.Println(ParseFamily("UNKNOWN"))
Output: transient transient
func (Family) DefaultFix ¶ added in v0.1.2
DefaultFix returns the default fix suggestion for this family.
func (Family) DefaultMessage ¶
DefaultMessage returns the default human-readable message for this family.
func (Family) DefaultWhy ¶ added in v0.1.2
DefaultWhy returns the default "why" explanation for this family.
func (Family) HTTPStatus ¶ added in v0.5.0
HTTPStatus returns the recommended HTTP response status code for this family. Use at HTTP/REST boundaries to translate a classified error into a response code:
Rejection → 400, Conflict → 409, Transient → 503, Corruption → 500, Infrastructure → 503, Orchestration → 500.
Invalid families return 500 (Internal Server Error).
Rationale ¶
The mapping reflects "whose fault is it, and what can the client do?":
- Rejection → 400 (Bad Request): the client sent bad input, lacked authorization, or asked for a missing resource. The client can fix this.
- Conflict → 409 (Conflict): the request conflicts with current state (duplicate, version mismatch). The client must refresh and reconcile.
- Transient → 503 (Service Unavailable): a temporary, retryable failure. The client SHOULD retry, ideally with backoff.
- Corruption → 500 (Internal Server Error): stored data is damaged — a data-integrity break that is the server's problem, not the client's. The client did nothing wrong and cannot fix it by retrying. (Earlier revisions mapped this to 422; 500 is correct because 422 implies the CLIENT submitted unprocessable data, which is not the case here.)
- Infrastructure → 503 (Service Unavailable): the system cannot serve the request right now (closed, misconfigured, starting up). Retryable in principle, though possibly requiring operator action.
- Orchestration → 500 (Internal Server Error): an internal coordination failure (bug, misconfiguration). Not retryable. The client did nothing wrong and cannot fix it by retrying.
Corruption and Infrastructure are distinguished by severity and audience (see Family.Severity and [Family.DefaultAudience]), not by HTTP status.
Example ¶
// Translate a family into an HTTP status code at REST boundaries.
err := NewConflict("order.duplicate", "order already exists")
fmt.Println(Classify(err).HTTPStatus())
Output: 409
func (Family) IsRetryable ¶
IsRetryable reports whether operations that encounter this error should be retried.
func (Family) IsValid ¶
IsValid reports whether the Family value is one of the six defined constants.
func (Family) MarshalText ¶ added in v0.4.0
MarshalText implements encoding.TextMarshaler for YAML/JSON config.
Example ¶
f := Transient text, _ := f.MarshalText() fmt.Println(string(text))
Output: transient
func (Family) RetryPolicy ¶ added in v0.5.0
func (f Family) RetryPolicy() RetryPolicy
RetryPolicy returns sensible retry defaults for this family. Only Transient is retryable; all other families return a single-attempt policy.
Example ¶
// RetryPolicy exposes advisory defaults — the library does not run the // retry loop. Only Transient gets more than a single attempt. fmt.Println(Transient.RetryPolicy().MaxAttempts) fmt.Println(Rejection.RetryPolicy().MaxAttempts)
Output: 3 1
func (Family) Severity ¶ added in v0.5.0
Severity returns a total order across families for multi-error classification. Higher = worse. Used by Classify to pick the worst sub-error of an errors.Join result deterministically, independent of argument order.
Transient(1) < Rejection(2) < Conflict(3) < Infrastructure(4) < Orchestration(5) < Corruption(6)
This preserves the fail-closed retry guarantee: if ANY sub-error is non-Transient (severity > 1), the joined error is non-Transient.
func (Family) String ¶
String returns the lowercase name of the Family (e.g. "transient", "rejection").
func (*Family) UnmarshalText ¶ added in v0.4.0
UnmarshalText implements encoding.TextUnmarshaler for YAML/JSON config. Unknown values are parsed as Transient (fail-open).
Example ¶
var f Family
_ = f.UnmarshalText([]byte("rejection"))
fmt.Println(f)
Output: rejection
type HTTPStatuser ¶ added in v0.8.0
HTTPStatuser errors carry a custom HTTP response status code that overrides the family-based default from Family.HTTPStatus. When HTTPStatus encounters an error implementing this interface with a non-zero status, that status wins.
This allows individual errors to deviate from their family's canonical HTTP status (e.g. a Rejection error that should be 404 Not Found instead of the family default 400 Bad Request) without changing the classification.
type HandleConfig ¶
type HandleConfig struct {
// Output is where human-readable messages are written. Defaults to os.Stderr.
Output io.Writer
// Registry provides classification sentinels and message templates.
// If nil, DefaultRegistry is used. Set this for test isolation or
// scoped error handling within a single binary.
Registry *Registry
// TemplateOverride overrides the default message template for a specific error code.
// Takes precedence over registered templates.
// map[errorCode]MessageTemplate
TemplateOverride map[string]MessageTemplate
// DiagnosticFunc runs diagnostics for the error. If nil, no diagnostics run.
// When set, diagnostics run automatically — no separate enable flag needed.
DiagnosticFunc DiagnosticFunc
// OnDiagnosed is called after diagnostics complete, before exit.
// Receives the error and diagnostic findings. Useful for logging/metrics.
OnDiagnosed func(err error, findings []DiagnosticFinding)
// Logger, when set, receives a structured log entry (family, code,
// retryable, exit_code, context.*) in the same call as human output —
// classifying once, logging once. Nil (default) skips structured logging,
// preserving the original behavior.
//
// This is the recommended hook for systemd/journald observability:
// every HandleError call emits a self-contained structured record without
// a separate LogError call.
Logger *slog.Logger
}
HandleConfig controls how HandleError processes an error at the CLI boundary.
type HandleResult ¶
type HandleResult struct {
// ExitCode is the process exit code derived from the error's Family.
ExitCode int
// Message is the human-readable error message.
Message string
// SuggestedFix is an actionable suggestion for resolving the error.
SuggestedFix string
}
HandleResult contains the full output of handling an error at the CLI boundary.
func HandleErrorDetailed ¶
func HandleErrorDetailed(err error) *HandleResult
HandleErrorDetailed returns a structured result without writing output. Useful for HTTP handlers, gRPC interceptors, and programmatic consumers.
Uses the same template resolution chain as HandleError: registered templates, built-in defaults, and family fallbacks.
Example ¶
err := NewRejection("file.not_found", "config missing").
WithContext("path", "/etc/app/config.yaml")
result := HandleErrorDetailed(err)
fmt.Println("exit:", result.ExitCode)
fmt.Println("fix:", result.SuggestedFix)
Output: exit: 1 fix: Check that the path and resource name are correct.
func HandleErrorDetailedWithConfig ¶ added in v0.3.0
func HandleErrorDetailedWithConfig(err error, cfg HandleConfig) *HandleResult
HandleErrorDetailedWithConfig returns a structured result with template overrides.
type HandlerFunc ¶ added in v0.6.0
type HandlerFunc func(http.ResponseWriter, *http.Request) error
HandlerFunc is an HTTP handler that returns an error. When the returned error is nil, the request is considered fully handled. A non-nil error is classified and translated into an HTTP error response by HTTPHandler.
Use this with HTTPHandler to bridge go-error-family classification into the net/http stack without per-handler boilerplate.
type MessageTemplate ¶
type MessageTemplate struct {
// What describes what happened. Supports {key} placeholders from error context.
What string
// Why explains why it happened. Supports {key} placeholders.
Why string
// Fix suggests how to resolve the error. Supports {key} placeholders.
Fix string
// WayOut provides an escape hatch or alternative action. Supports {key} placeholders.
WayOut string
}
MessageTemplate defines the Wix-style presentation for an error code. Based on the Wix UX framework: What / Why / Fix / WayOut.
func TemplateForCode ¶ added in v0.6.0
func TemplateForCode(code string) (MessageTemplate, bool)
TemplateForCode resolves a MessageTemplate for an error code, checking registered templates on DefaultRegistry first, then built-in defaults. Returns (zero, false) when no template exists for the code.
Delegates to DefaultRegistry. For scoped lookups, use Registry.TemplateForCode on a custom Registry.
type Registry ¶ added in v0.5.0
type Registry struct {
// contains filtered or unexported fields
}
Registry holds classification sentinels and message templates. It is safe for concurrent use. The zero value is not usable — use NewRegistry.
A Registry solves two problems that package-level globals cannot:
- Test isolation: each test can construct its own Registry without polluting global state or needing t.Cleanup(Unregister…) boilerplate.
- Scoped overrides: a service can register sentinels or templates for its own error handling without affecting other parts of the same binary.
The package-level DefaultRegistry is used by the convenience functions (Classify, RegisterClassification, RegisterTemplate, etc.) and by the HandleError family when no custom Registry is provided in HandleConfig.
Sentinels are stored behind an atomic.Pointer to an immutable snapshot: the hot read path (every Classify) is lock-free and allocation-free, while rare writes serialize under the write lock and publish a new snapshot via copy-on-write.
func NewRegistry ¶ added in v0.5.0
func NewRegistry() *Registry
NewRegistry creates an empty Registry ready for use.
Example ¶
// A custom Registry enables test isolation and scoped handling without
// touching the package-level DefaultRegistry.
reg := NewRegistry()
reg.RegisterClassification(errors.New("sentinel"), Transient)
fmt.Println(reg.Classify(errors.New("sentinel")))
Output: transient
func (*Registry) Classify ¶ added in v0.5.0
Classify returns the Family of any error using this registry's sentinel mappings.
Checks in order, first match wins:
- Multi-error (errors.Join) — first non-Transient sub-error wins
- Classified interface — the error itself declares its family
- Retryable interface — infer from retryability
- Registered sentinels in this registry
- Registered classifiers (predicate-based, for dynamic third-party errors)
- Default — Transient (fail-open for retry)
Returns Rejection for nil errors.
func (*Registry) Clone ¶ added in v0.5.0
Clone returns a new Registry with a deep copy of this Registry's sentinels and templates. Mutations to the clone do not affect the original and vice versa. Enables inherit-and-extend patterns: start from DefaultRegistry, clone it, and register scope-specific overrides without touching the global.
func (*Registry) RegisterClassification ¶ added in v0.5.0
RegisterClassification maps a third-party sentinel error to a Family. Thread-safe. Call from init() in external packages:
func init() {
errorfamily.DefaultRegistry.RegisterClassification(sql.ErrConnDone, errorfamily.Transient)
}
This is for errors you don't own (stdlib, libraries). For your own errors, implement the Classified interface instead.
func (*Registry) RegisterClassifications ¶ added in v0.5.0
RegisterClassifications registers multiple sentinel-to-Family mappings at once. Thread-safe.
func (*Registry) RegisterClassifier ¶ added in v0.6.0
func (r *Registry) RegisterClassifier(c Classifier)
RegisterClassifier adds a predicate-based Classifier for dynamic third-party errors that cannot be registered as sentinels (e.g. *sqlite.Error). Thread-safe. Classifiers run after sentinel matching fails, in registration order; the first returning ok=true wins.
Because Go func values are not comparable, individual classifiers cannot be unregistered. For test isolation or scoped handling, construct a NewRegistry rather than polluting DefaultRegistry.
func (*Registry) RegisterClassifiers ¶ added in v0.6.0
func (r *Registry) RegisterClassifiers(classifiers ...Classifier)
RegisterClassifiers adds multiple predicate-based Classifier funcs at once. Thread-safe. Classifiers run in registration order.
func (*Registry) RegisterTemplate ¶ added in v0.5.0
func (r *Registry) RegisterTemplate(code string, tmpl MessageTemplate)
RegisterTemplate adds a MessageTemplate for a specific error code. Thread-safe. Overrides any existing template for the same code.
func (*Registry) RegisterTemplates ¶ added in v0.5.0
func (r *Registry) RegisterTemplates(templates map[string]MessageTemplate)
RegisterTemplates registers multiple code-to-template mappings at once. Thread-safe. Keys are lower-cased for case-insensitive lookup. Parity with Registry.RegisterClassifications for the sentinel side.
func (*Registry) TemplateForCode ¶ added in v0.6.0
func (r *Registry) TemplateForCode(code string) (MessageTemplate, bool)
TemplateForCode resolves a MessageTemplate for an error code using this registry, checking registered templates first, then built-in defaults. Returns (zero, false) when no template exists for the code.
This is the public counterpart to the internal resolution used by HandleError: it lets HTTP/REST consumers look up a registered template without reimplementing the lookup or wiring the full CLI pipeline.
func (*Registry) UnregisterClassification ¶ added in v0.5.0
UnregisterClassification removes a previously registered sentinel mapping. Thread-safe. No-op if the sentinel has no registered classification.
func (*Registry) UnregisterTemplate ¶ added in v0.5.0
UnregisterTemplate removes a previously registered template. Thread-safe. No-op if the code has no registered template.
type RetryPolicy ¶ added in v0.5.0
RetryPolicy holds retry parameters derived from a Family. It is advisory: the library does not implement the retry loop — consumers (or retry libraries like failsafe-go) use these as sensible starting defaults.
MaxAttempts is the total number of attempts including the first. Non-retryable families return MaxAttempts=1 (execute once, never retry).
type Retryable ¶
Retryable errors explicitly declare whether they should be retried. Overrides Family-based retry decisions when present.
type Tone ¶
type Tone string
Tone is a presentation tone hint for error messages. Used by the presentation layer to choose appropriate language.
const ( // ToneInstructional guides the user on how to fix their input. ToneInstructional Tone = "instructional" // ToneExplanatory explains what happened and why. ToneExplanatory Tone = "explanatory" // ToneReassuring reassures the user that the issue is temporary. ToneReassuring Tone = "reassuring" // ToneUrgent signals that immediate action is required. ToneUrgent Tone = "urgent" // ToneApologetic indicates the system is at fault. ToneApologetic Tone = "apologetic" )
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
agent
module
|
|
|
bridge
module
|
|
|
diagnose
module
|
|
|
Package errorfamilytest provides assertion helpers for testing code that returns go-error-family classified errors.
|
Package errorfamilytest provides assertion helpers for testing code that returns go-error-family classified errors. |
|
examples
module
|