errors

package module
v1.3.0 Latest Latest
Warning

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

Go to latest
Published: Apr 18, 2026 License: MIT Imports: 18 Imported by: 11

README

errors — production-grade error handling for Go

Go Reference Go Report Card License

A feature-complete error handling library for Go. Fully compatible with errors.Is, errors.As, and errors.Unwrap. Optimised for high-throughput systems with object pooling, hybrid context storage, and inlining-immune stack capture.


Contents


Installation

go get github.com/olekukonko/errors@latest

Requires Go 1.21 or later.


Package overview

Package Purpose
errors Core error type, wrapping, context, stack traces, retry, chain, multi-error, channel utilities
errmgr Parameterised error templates, occurrence monitoring, threshold alerting

Core — errors

Creating errors
// Fast — no stack trace, 0 allocations with pooling
err := errors.New("connection failed")

// Formatted — full fmt verb support including %w
err := errors.Newf("user %s not found", "alice")
err := errors.Errorf("query failed: %w", cause) // alias of Newf

// With stack trace
err := errors.Trace("critical issue")
err := errors.Tracef("query %s failed: %w", query, cause)

// Named — useful for sentinel-style matching
err := errors.Named("AuthError")

// Standard library compatible
err := errors.Std("connection failed")   // returns plain error
err := errors.Stdf("error %s", "detail") // formatted plain error
Stack traces
// Capture at creation
err := errors.Trace("critical issue")

// Add to an existing error
err = err.WithStack()

// Read frames
for _, frame := range err.Stack() {
    fmt.Println(frame) // "main.go:42 main.main"
}

// Lightweight version (file:line only, no function names)
for _, frame := range err.FastStack() {
    fmt.Println(frame)
}

Stack capture is immune to compiler inlining — frames are collected from the physical call stack and trimmed by slice arithmetic, not by skip count.

Context
err := errors.New("processing failed").
    With("user_id", "123").
    With("attempt", 3).
    With("retryable", true)

// Read back
ctx := errors.Context(err) // map[user_id:123 attempt:3 retryable:true]

// Check for a key
if err.HasContextKey("user_id") { ... }

// Variadic bulk attach
err.With("k1", v1, "k2", v2)

// Semantic helpers
err.WithCode(500)
err.WithCategory("network")
err.WithTimeout()
err.WithRetryable()

The first four context items are stored in a fixed-size array (no allocation). Items beyond four spill to a map.

Wrapping and chaining
lowErr  := errors.New("connection timeout").With("server", "db01")
bizErr  := errors.New("failed to load user").Wrap(lowErr)
apiErr  := errors.Wrapf(bizErr, "request failed: %w", bizErr)

// Traverse
for i, e := range errors.UnwrapAll(apiErr) {
    fmt.Printf("%d. %s\n", i+1, e)
}
// 1. request failed: ...
// 2. failed to load user
// 3. connection timeout
Sentinel errors

Const creates a stable, pointer-comparable sentinel safe for package-level variables.

var (
    ErrNotFound  = errors.Const("not_found",  "resource not found")
    ErrForbidden = errors.Const("forbidden",  "access denied")
)

// Match anywhere in a chain
if errors.Is(err, ErrNotFound) { ... }

// Add call-site context without losing the sentinel
err := ErrNotFound.With("user 42 not found")
errors.Is(err, ErrNotFound) // true — sentinel is the cause

// JSON and slog work automatically
b, _ := json.Marshal(ErrNotFound)   // {"error":"resource not found","code":"not_found"}
slog.Error("lookup failed", "err", ErrNotFound)

Const vs errmgr.Define errors.Const — static comparable value for errors.Is matching. errmgr.Define — parameterised factory that creates new *Error instances from a format template.

Type assertions — Is / As
// Is — checks identity or name match
err := errors.Named("AuthError")
wrapped := errors.Wrapf(err, "login failed")
errors.Is(wrapped, err) // true

// As — extract the first matching *Error from the chain
var target *errors.Error
if errors.As(wrapped, &target) {
    fmt.Println(target.Name()) // "AuthError"
}

// Generic helpers (Go 1.18+)
if e, ok := errors.AsType[*MyError](err); ok { ... }
if errors.IsType[*MyError](err) { ... }

found, ok := errors.FindType(err, func(e *MyError) bool {
    return e.Code() == 404
})

codes := errors.Map(err, func(e *MyError) int { return e.Code() })
errors.Filter[*MyError](err)       // []  *MyError from chain
errors.FirstOfType[*MyError](err)  // first *MyError

Is() string-equality note(*Error).Is falls back to string comparison as a convenience for matching stdlib errors by message. For strict identity matching use Const().

Multi-error aggregation
// Basic
m := errors.NewMultiError()
m.Add(errors.New("name required"))
m.Add(errors.New("email invalid"))
fmt.Println(m.Count()) // 2

// With limits and sampling
m := errors.NewMultiError(
    errors.WithLimit(100),
    errors.WithSampling(10), // 10% sample rate
)

// Custom formatter
m := errors.NewMultiError(
    errors.WithFormatter(func(errs []error) string {
        return fmt.Sprintf("%d errors", len(errs))
    }),
)

// Inspect
m.First()   // first error
m.Last()    // last error
m.Errors()  // []error snapshot
m.Has()     // bool
m.Single()  // nil | first error | *MultiError

// Filter
networkErrs := m.Filter(func(e error) bool {
    return strings.Contains(e.Error(), "network")
})

// Merge two MultiErrors
m.Merge(other)

// Join is a convenience that collapses errors to *MultiError or nil
err := errors.Join(err1, err2, err3)
Retry
retry := errors.NewRetry(
    errors.WithMaxAttempts(5),
    errors.WithDelay(200*time.Millisecond),
    errors.WithMaxDelay(2*time.Second),
    errors.WithJitter(true),
    errors.WithBackoff(errors.ExponentialBackoff{}),
    errors.WithRetryIf(errors.IsRetryable),
    errors.WithOnRetry(func(attempt int, err error) {
        log.Printf("attempt %d: %v", attempt, err)
    }),
)

err := retry.Execute(func() error {
    return callExternalService()
})

// Generic version — preserves return value
result, err := errors.ExecuteReply[string](retry, func() (string, error) {
    return fetchData()
})

// Context-aware
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
retry2 := retry.Transform(errors.WithContext(ctx))
err = retry2.Execute(fn)

// Backoff strategies
errors.ConstantBackoff{}
errors.LinearBackoff{}
errors.ExponentialBackoff{}
Chain execution

Sequential steps with per-step retry, timeout, tagging, and optional steps.

chain := errors.NewChain(
    errors.ChainWithTimeout(10*time.Second),
    errors.ChainWithLogHandler(slog.Default().Handler()),
).
    Step(validateInput).Tag("validation").
    Step(verifyKYC).Tag("kyc").
    Step(processPayment).Tag("billing").Code(402).
        Retry(3, 100*time.Millisecond, errors.WithRetryIf(errors.IsRetryable)).
    Step(sendNotification).Tag("notification").Optional()

if err := chain.Run(); err != nil {
    errors.Inspect(err, os.Stderr)
}

// Run all steps, collect every error
if err := chain.RunAll(); err != nil {
    errors.Inspect(err, os.Stderr)
}

StepCtx passes the chain-level context (with its deadline) to the step, so blocking calls like HTTP or database queries respect the chain timeout:

chain.StepCtx(func(ctx context.Context) error {
    req, _ := http.NewRequestWithContext(ctx, "GET", url, nil)
    _, err := http.DefaultClient.Do(req)
    return err
})
Channel utilities and streaming
<-chan error utilities

These compose with the standard Go (chan T, chan error) idiom rather than replacing it.

// Drain — block until channel closes, collect into *MultiError
err := errors.Drain(errs)

// First — return first non-nil error; ctx for deadline only, caller owns cancel
err := errors.First(ctx, errs)
if err != nil {
    cancel() // caller decides to stop siblings
}

// Collect — bounded sample; wraps ErrLimitReached when n is hit
err := errors.Collect(ctx, errs, 10)
if errors.Is(err, errors.ErrLimitReached) {
    log.Warn("more than 10 errors — some dropped")
}

// Fan — merge multiple error channels; caller must drain or cancel to avoid leak
merged := errors.Fan(ctx, validateErrs, enrichErrs)
for err := range merged {
    log.Println(err)
}
Stream — concurrent item processing
// Process items concurrently, collect all errors
s := errors.NewStream(ctx, urls, func(url string) error {
    return fetch(url)
}, 8) // 8 workers; omit for len(items) workers

// Option A — block until done
if err := s.Wait(); err != nil {
    errors.Inspect(err, os.Stderr)
}

// Option B — process errors as they arrive
s.Each(func(err error) {
    log.Println(err)
})

// Stop early (drains channel to avoid goroutine leak)
s.Stop()

Wait and Each are mutually exclusive. Calling either a second time panics immediately.

HTTP helpers
// Resolve HTTP status from an *Error's code
status := errors.HTTPStatusCode(err, http.StatusInternalServerError)

// Write HTTP error response
errors.HTTPError(w, err) // plain text, status from err.Code()

// With options
errors.HTTPError(w, err,
    errors.WithFallbackCode(http.StatusBadGateway),
    errors.WithBody(false),        // header only
    errors.WithBodyFunc(func(e error) string {
        return fmt.Sprintf(`{"error":%q}`, e.Error())
    }),
)
Concurrent group

Group collects all errors from concurrent goroutines — unlike errgroup which stops at the first.

g := errors.NewGroup()

g.Go(func() error { return validateUser(id) })
g.Go(func() error { return validatePerms(id) })

if err := g.Wait(); err != nil {
    // err is *MultiError containing every failure
    errors.Inspect(err, os.Stderr)
}

// Context-aware
g := errors.NewGroup(
    errors.GroupWithContext(ctx, true), // cancelOnFirst=true
    errors.GroupWithLimit(50),
)

g.GoCtx(func(ctx context.Context) error {
    return longRunningCheck(ctx)
})

_ = g.Wait()
Inspect
// Default — writes to os.Stderr
errors.Inspect(err)

// Targeted output
var buf bytes.Buffer
errors.Inspect(err, &buf)

// Multiple destinations
errors.Inspect(err, os.Stderr, logFile)

// Options
errors.Inspect(err, os.Stderr,
    errors.WithStackFrames(5),
    errors.WithMaxDepth(20),
)

// *Error-specific convenience
errors.InspectError(err, os.Stderr)

Inspect handles *Error, *MultiError, and any stdlib error. It writes to the supplied io.Writer values (merged via io.MultiWriter) and never touches stdout.

slog integration

Both *Error and *Sentinel implement slog.LogValuer:

slog.Error("request failed", "err", err)
// produces structured group: err.message, err.name, err.code, err.category, err.context, err.cause

slog.Error("lookup failed", "err", errors.ErrNotFound)
// produces: err.error="resource not found", err.code="not_found"
Pool management
// Pre-warm (called automatically at init with 100 instances)
errors.WarmPool(1000)
errors.WarmStackPool(500)

// Tune global config
errors.Configure(errors.Config{
    StackDepth:     32,
    ContextSize:    4,
    DisablePooling: false,
    FilterInternal: true,
    AutoFree:       false, // opt-in GC-based pool return
})

// Explicit pool return (preferred)
err := errors.New("temp")
defer err.Free()

// Copy without affecting original
copied := err.Copy().With("extra", "data")

// Transform (non-destructive)
enriched := errors.Transform(err, func(e *errors.Error) {
    e.WithCode(500).With("env", "prod").WithStack()
})

Management — errmgr

Parameterised error templates
// Define a reusable template
var ErrDBQuery = errmgr.Define("DBQuery", "database query failed: %s")

// Instantiate with arguments
err := ErrDBQuery("SELECT timed out")
fmt.Println(err)            // "database query failed: SELECT timed out"
fmt.Println(err.Category()) // "database"
Predefined errors
err := errmgr.ErrNotFound
fmt.Println(err.Code()) // 404

err := errmgr.ErrDBQuery("SELECT failed")
Threshold monitoring
netErr := errmgr.Define("NetError", "network issue: %s")
monitor := errmgr.NewMonitor("NetError")
errmgr.SetThreshold("NetError", 3)
defer monitor.Close()

go func() {
    for alert := range monitor.Alerts() {
        fmt.Printf("alert: %s (count: %d)\n", alert, alert.Count())
    }
}()

err := netErr("timeout")
err.Free()

Key design decisions:

  • PoolNew and Wrap reuse *Error instances from sync.Pool (12 ns/op, 0 allocs).
  • Hybrid context — up to 4 key-value pairs in a fixed array; overflow to map. Avoids heap allocation for the common case.
  • Stack capturecaptureStack is inlining-immune: it always starts from runtime.Callers frame 1 and trims by array slicing, so the compiler's inlining decisions never corrupt the skip count.
  • Pool capacity preservation — the pool buffer is trimmed in-place (copy(buf, buf[trimmed:n])), not re-allocated. Prevents progressive capacity shrinkage under repeated Free() cycles.
  • MarshalJSON — bytes are copied out of the pool buffer before returning it, eliminating the race between concurrent JSON serialisations.
  • With() — the mutex is acquired once at entry, eliminating the TOCTOU race in the former optimistic read-then-lock path.

Migration guide

From standard library
// Before
err := fmt.Errorf("user %s not found: %w", username, cause)

// After — same output, plus context, code, and chain traversal
err := errors.Newf("user %s not found: %w", username, cause).
    With("username", username).
    WithCode(404)
From pkg/errors
// Before
err := pkgerrors.Wrap(cause, "operation failed")

// After
err := errors.New("operation failed").Wrap(cause).WithStack()
Stdlib errors.Is / errors.As compatibility
// Fully compatible — no changes needed
if errors.Is(err, io.EOF) { ... }

var target *errors.Error
if errors.As(err, &target) {
    fmt.Println(target.Name())
}

FAQ

When should I use Const vs Named? Const — package-level sentinel for errors.Is matching. Returns the same pointer every call, so pointer equality works. Named — creates a new *Error instance each call; useful for structured errors with context but not for == comparison.

When should I use Const vs errmgr.Define? errors.Const("not_found", "resource not found") creates a static sentinel. errmgr.Define("DBQuery", "query failed: %s") creates a parameterised factory — you call it with arguments to produce a new *Error each time.

When should I call Free()? In hot paths where the error is short-lived and you want to return it to the pool immediately. For most application code, letting the GC handle it is fine. If AutoFree is enabled in Config, the GC returns the error automatically — but defer err.Free() is more predictable.

Why does First not cancel the context? context.Context is immutable — only context.WithCancel produces a cancellable context. First accepts ctx for deadline support only. The pattern is: call First, then call cancel() yourself if you want to stop siblings.

Why do Each and Wait on Stream panic on second call? Consuming the same channel twice silently splits errors between two callers. The panic surfaces the bug immediately rather than letting it produce subtly wrong results in production.

How do I debug a deep error chain?

errors.Inspect(err, os.Stderr, errors.WithMaxDepth(30), errors.WithStackFrames(10))

How do I write to both stderr and a log file?

errors.Inspect(err, os.Stderr, logFile) // io.MultiWriter internally

Contributing

Fork → branch → commit → PR. Please include tests for new behaviour and run go test -count=10 -race ./... before opening a PR.

License

MIT — see LICENSE.

Documentation

Overview

Package errors provides a robust error handling library with support for error wrapping, stack traces, context storage, and retry mechanisms. It extends the standard library's error interface with features like HTTP-like status codes, error categorization, and JSON serialization, while maintaining compatibility with `errors.Is`, `errors.As`, and `errors.Unwrap`. The package is thread-safe and optimized with object pooling for performance.

Package errors provides utilities for error handling, including a flexible retry mechanism.

Index

Constants

View Source
const (
	DefaultCode = 500 // Default HTTP status code for errors if not specified.
)

Constants defining default configuration and context keys.

Variables

View Source
var ErrLimitReached = Const("limit_reached", "error collection limit reached")

ErrLimitReached is returned by Collect when n errors have been gathered before the channel closed or the context was done. Callers can use errors.Is(err, ErrLimitReached) to distinguish this case.

Functions

func As

func As(err error, target interface{}) bool

As wraps errors.As, using custom type assertion for *Error types. Falls back to standard errors.As for non-*Error types. Returns false if either err or target is nil.

func AsType added in v1.3.0

func AsType[T error](err error) (T, bool)

AsType attempts to find the first error in the chain that matches type T. Returns the matched error and true if found, otherwise zero value and false.

func Caller

func Caller(skip int) (file string, line int, function string)

Caller returns the file, line, and function name of the caller at skip level. Skip=0 returns the caller of this function, 1 returns its caller, etc.

func Category

func Category(err error) string

Category returns the category of an error, if it is an *Error. Returns an empty string for non-*Error types or unset categories.

func Code

func Code(err error) int

Code returns the status code of an error, if it is an *Error. Returns 500 as a default for non-*Error types to indicate an internal error.

func Collect added in v1.3.0

func Collect(ctx context.Context, ch <-chan error, n int) error

Collect reads up to n non-nil errors from ch (or until ch closes or ctx is done) and returns them as a *MultiError.

If the limit n is reached before the channel closes, the returned error wraps ErrLimitReached as its cause so callers can distinguish the two cases:

err := errors.Collect(ctx, errs, 10)
if errors.Is(err, errors.ErrLimitReached) {
    // stopped early — more errors may exist
}

func Configure

func Configure(cfg Config)

Configure updates the global configuration for the errors package. It is thread-safe and should be called early to avoid race conditions. Changes apply to all subsequent error operations. Example:

errors.Configure(errors.Config{StackDepth: 16, DisablePooling: true})

func Contains added in v1.3.0

func Contains(err error, targets ...error) bool

Contains checks if any error in the chain matches any of the target errors. Uses our package's Is function for matching.

func Context

func Context(err error) map[string]interface{}

Context extracts the context map from an error, if it is an *Error. Returns nil for non-*Error types or if no context is present.

func Count

func Count(err error) uint64

Count returns the occurrence count of an error, if it is an *Error. Returns 0 for non-*Error types.

func Drain added in v1.3.0

func Drain(ch <-chan error) error

Drain reads all errors from ch until it is closed and returns them as a *MultiError. Returns nil if every received value was nil. Blocks until ch is closed.

Example:

results, errs := processItems(ctx, items)
if err := errors.Drain(errs); err != nil {
    log.Println(err)
}

func ExecuteReply

func ExecuteReply[T any](r *Retry, fn func() (T, error)) (T, error)

ExecuteReply runs the provided function with retry logic and returns its result. Returns the result and nil on success, or zero value and last error on failure; generic type T.

func Fan added in v1.3.0

func Fan(ctx context.Context, chans ...<-chan error) <-chan error

Fan merges multiple error channels into a single output channel that closes when all inputs have closed or ctx is done.

Callers MUST either drain the returned channel to completion OR cancel ctx — failing to do so leaks the internal goroutines. The select in each forwarder respects ctx.Done() so cancellation is always safe.

Example:

errs1, errs2 := stage1(ctx), stage2(ctx)
for err := range errors.Fan(ctx, errs1, errs2) {
    if err != nil { log.Println(err) }
}

func Filter added in v1.3.0

func Filter[T error](err error) []T

Filter returns a slice of all errors of type T from the error chain.

func Find

func Find(err error, pred func(error) bool) error

Find searches the error chain for the first error matching pred. Returns nil if no match is found or pred is nil; traverses both Unwrap() and Cause() chains.

func FindType added in v1.3.0

func FindType[T error](err error, predicate func(T) bool) (T, bool)

FindType returns the first error in the chain of type T that satisfies the predicate.

func First added in v1.3.0

func First(ctx context.Context, ch <-chan error) error

First returns the first non-nil error received from ch, then returns. Uses ctx for deadline/cancellation only — it does NOT call any cancel function. The caller is responsible for cancelling sibling work after First returns.

Returns nil if ch is closed before any error arrives, or ctx is done. Returns ctx.Err() if the context is cancelled or times out.

Example:

ctx, cancel := context.WithCancel(context.Background())
results, errs := processItems(ctx, items)
if err := errors.First(ctx, errs); err != nil {
    cancel() // caller decides to stop siblings
    log.Println(err)
}
defer cancel()

func FirstOfType added in v1.3.0

func FirstOfType[T error](err error) (T, bool)

FirstOfType returns the first error in the chain of type T.

func FmtErrorCheck

func FmtErrorCheck(format string, args ...interface{}) (result string, err error)

FmtErrorCheck safely formats a string using fmt.Sprintf, catching panics. Returns the formatted string and any error encountered. Internal use by Newf to validate format strings. Example:

result, err := FmtErrorCheck("value: %s", "test")

func FormatError

func FormatError(err error) string

FormatError returns a formatted string representation of an error.

func HTTPError added in v1.3.0

func HTTPError(w http.ResponseWriter, err error, opts ...HTTPOption)

HTTPError writes err to w as an HTTP error response.

Status code resolution (first match wins): err is *Error with Code() in the valid HTTP range (100–599) WithFallbackCode option (default 500)

Content-Type defaults to text/plain unless WithBodyFunc provides content that implies a different type (caller must set the header themselves in that case — use WithBodyFunc + manual header setting).

Example — simplest usage, plain text body, 500 fallback:

errors.HTTPError(w, err)

Example — custom fallback status:

errors.HTTPError(w, err, errors.WithFallbackCode(http.StatusBadGateway))

Example — suppress body (header only):

errors.HTTPError(w, err, errors.WithBody(false))

Example — JSON body:

errors.HTTPError(w, err,
    errors.WithBodyFunc(func(e error) string {
        return fmt.Sprintf(`{"error":%q,"code":%d}`,
            e.Error(), errors.HTTPStatusCode(e, 500))
    }),
)

func HTTPStatusCode added in v1.3.0

func HTTPStatusCode(err error, defaultCode int) int

HTTPStatusCode returns the HTTP status code embedded in err. If err is nil, has no code, or the code is outside the valid HTTP range (100–599), defaultCode is returned.

Example:

status := errors.HTTPStatusCode(err, http.StatusInternalServerError)

func Has

func Has(err error) bool

Has checks if an error contains meaningful content. Returns true for non-nil standard errors or *Error with content (msg, name, template, or cause).

func HasContextKey

func HasContextKey(err error, key string) bool

HasContextKey checks if the error's context contains the specified key. Returns false for non-*Error types or if the key is not present in the context.

func Inspect

func Inspect(err error, targets ...interface{})

Inspect writes a human-readable description of err to each writer in ws. If no writers are supplied it defaults to os.Stderr. Multiple writers are combined with io.MultiWriter so a single call can write to a log file and a buffer simultaneously.

Example — default (stderr):

errors.Inspect(err)

Example — write to a buffer for testing:

var buf bytes.Buffer
errors.Inspect(err, &buf)

Example — write to both stderr and a file:

errors.Inspect(err, os.Stderr, logFile)

Example — customise stack depth:

errors.Inspect(err, os.Stderr, errors.WithStackFrames(5))

Note: InspectOption values must come after all io.Writer values. Any value that is neither an io.Writer nor an InspectOption is silently ignored.

func InspectError

func InspectError(err *Error, targets ...interface{})

InspectError is a convenience wrapper for *Error that calls Inspect. Kept for backwards compatibility; prefer Inspect for new code.

func Is

func Is(err, target error) bool

Is wraps errors.Is, using custom matching for *Error types. Falls back to standard errors.Is for non-*Error types; returns true if err equals target.

func IsEmpty

func IsEmpty(err error) bool

IsEmpty checks if an error has no meaningful content. Returns true for nil errors, empty *Error instances, or standard errors with whitespace-only messages.

func IsError

func IsError(err error) bool

IsError checks if an error is an instance of *Error. Returns true only for this package's custom error type; false for nil or other types.

func IsNull

func IsNull(err error) bool

IsNull checks if an error is nil or represents a NULL value. Delegates to *Error’s IsNull for custom errors; uses sqlNull for others.

func IsRetryable

func IsRetryable(err error) bool

IsRetryable checks if an error is retryable. For *Error, checks context for retry flag; for others, looks for "retry" or timeout in message. Returns false for nil errors; thread-safe for *Error types.

func IsTimeout

func IsTimeout(err error) bool

IsTimeout checks if an error indicates a timeout. For *Error, checks context for timeout flag; for others, looks for "timeout" in message. Returns false for nil errors.

func IsType added in v1.3.0

func IsType[T error](err error) bool

IsType checks if the error or any error in its chain is of type T.

func Join added in v1.3.0

func Join(errs ...error) error

Join returns an error that wraps the given errors. Any nil error values are discarded. Join returns nil if every error is nil. The error formats as the concatenation of the errors' strings, separated by newlines. The resulting error implements Unwrap() []error if multiple non-nil errors are present.

func JoinErrors added in v1.3.0

func JoinErrors(errs []error, keyValues ...interface{}) error

JoinErrors joins multiple errors using Join and wraps them with context. Returns nil if all errors are nil.

func Map added in v1.3.0

func Map[T error, R any](err error, fn func(T) R) []R

Map applies a transformation function to each error of type T in the chain.

func Name

func Name(err error) string

Name returns the name of an error, if it is an *Error. Returns an empty string for non-*Error types or unset names.

func Reduce added in v1.3.0

func Reduce[T error, R any](err error, initial R, fn func(T, R) R) R

Reduce walks the error chain and accumulates a result for errors of type T.

func Stack

func Stack(err error) []string

Stack extracts the stack trace from an error, if it is an *Error. Returns nil for non-*Error types or if no stack is present.

func Std

func Std(text string) error

Std creates a standard error using errors.New for compatibility. Does not capture stack traces or add context. Example:

err := errors.Std("simple error")

func Stdf

func Stdf(format string, a ...interface{}) error

Stdf creates a formatted standard error using fmt.Errorf for compatibility. Supports %w for wrapping; does not capture stack traces. Example:

err := errors.Stdf("failed: %w", cause)

func Unwrap

func Unwrap(err error) error

Unwrap returns the underlying cause of an error, if it implements Unwrap. For *Error, returns cause; for others, returns the error itself; nil if err is nil.

func UnwrapAll

func UnwrapAll(err error) []error

UnwrapAll returns a slice of all errors in the chain, including the root error. Traverses both Unwrap() and Cause() chains; returns nil if err is nil.

func Walk

func Walk(err error, fn func(error))

Walk traverses the error chain, applying fn to each error. Supports both Unwrap() and Cause() interfaces; stops at nil or non-unwrappable errors.

func WarmPool

func WarmPool(count int)

WarmPool pre-populates the error pool with count instances. Improves performance by reducing initial allocations. No-op if pooling is disabled. Example:

errors.WarmPool(1000)

func WarmStackPool

func WarmStackPool(count int)

WarmStackPool pre-populates the stack pool with count slices. Improves performance for stack-intensive operations. No-op if pooling is disabled. Example:

errors.WarmStackPool(500)

func With

func With(err error, key string, value interface{}) error

With adds a key-value pair to an error's context, if it is an *Error. Returns the original error unchanged if not an *Error; no-op for non-*Error types.

Types

type BackoffStrategy

type BackoffStrategy interface {
	// Backoff returns the delay for a given attempt based on the base delay.
	Backoff(attempt int, baseDelay time.Duration) time.Duration
}

BackoffStrategy defines the interface for calculating retry delays.

type Chain

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

Chain executes functions sequentially with enhanced error handling. Logging is optional and configured via a slog.Handler.

func NewChain

func NewChain(opts ...ChainOption) *Chain

NewChain creates a new Chain with the given options. Logging is disabled by default (logHandler is nil).

func (*Chain) Call

func (c *Chain) Call(fn interface{}, args ...interface{}) *Chain

Call adds a step by wrapping a function with arguments. It uses reflection to validate and invoke the function.

func (*Chain) Code

func (c *Chain) Code(code int) *Chain

Code sets a numeric error code for the last step.

func (*Chain) Errors

func (c *Chain) Errors() []error

Errors returns a copy of the collected errors.

func (*Chain) HasErrors

func (c *Chain) HasErrors() bool

HasErrors checks if any errors were collected.

func (*Chain) LastError

func (c *Chain) LastError() error

LastError returns the most recent error or nil if none exist.

func (*Chain) Len

func (c *Chain) Len() int

Len returns the number of steps in the chain.

func (*Chain) LogOnFail

func (c *Chain) LogOnFail() *Chain

LogOnFail enables automatic logging of errors for the last step.

func (*Chain) MaxErrors

func (c *Chain) MaxErrors(max int) *Chain

MaxErrors sets the maximum number of errors allowed.

func (*Chain) Optional

func (c *Chain) Optional() *Chain

Optional marks the last step as optional. Optional steps don't stop the chain on error.

func (*Chain) Reset

func (c *Chain) Reset()

Reset clears the chain's steps, errors, and context.

func (*Chain) Retry

func (c *Chain) Retry(maxAttempts int, delay time.Duration, opts ...RetryOption) *Chain

Retry configures retry behavior for the last step. Retry configures retry behavior for the last step.

func (*Chain) Run

func (c *Chain) Run() error

Run executes the chain, stopping on the first non-optional error. It returns the first error encountered or nil if all steps succeed.

func (*Chain) RunAll

func (c *Chain) RunAll() error

RunAll executes all steps, collecting errors without stopping. It returns a MultiError containing all errors or nil if none occurred.

func (*Chain) Step

func (c *Chain) Step(fn func() error) *Chain

Step adds a new step to the chain with the provided function. The function must return an error or nil.

func (*Chain) StepCtx added in v1.3.0

func (c *Chain) StepCtx(fn func(ctx context.Context) error) *Chain

StepCtx adds a context-aware step to the chain. The provided function receives the chain's context (which carries any chain-level deadline/timeout), so cancellation and timeouts propagate correctly into blocking operations such as HTTP requests, database queries, or gRPC calls.

StepCtx is the context-safe alternative to Step; existing Step calls are unchanged and fully compatible.

Example:

chain := NewChain(ChainWithTimeout(5 * time.Second)).
	StepCtx(func(ctx context.Context) error {
		req, _ := http.NewRequestWithContext(ctx, "GET", url, nil)
		_, err := http.DefaultClient.Do(req)
		return err
	})

func (*Chain) Tag

func (c *Chain) Tag(category ErrorCategory) *Chain

Tag sets an error category for the last step.

func (*Chain) Timeout

func (c *Chain) Timeout(d time.Duration) *Chain

Timeout sets a timeout for the entire chain. Thread-safe: protected by configMu.

func (*Chain) Unwrap

func (c *Chain) Unwrap() []error

Unwrap returns the collected errors (alias for Errors).

func (*Chain) With

func (c *Chain) With(key string, value interface{}) *Chain

With adds a key-value pair to the last step's context.

func (*Chain) WithLog

func (c *Chain) WithLog(attrs ...slog.Attr) *Chain

WithLog adds logging attributes to the last step.

type ChainOption

type ChainOption func(*Chain)

ChainOption defines a function that configures a Chain.

func ChainWithAutoWrap

func ChainWithAutoWrap(auto bool) ChainOption

ChainWithAutoWrap enables or disables automatic error wrapping.

func ChainWithLogHandler

func ChainWithLogHandler(handler slog.Handler) ChainOption

ChainWithLogHandler sets a custom slog.Handler for logging. If handler is nil, logging is effectively disabled.

func ChainWithMaxErrors

func ChainWithMaxErrors(max int) ChainOption

ChainWithMaxErrors sets the maximum number of errors allowed. A value <= 0 means no limit.

func ChainWithTimeout

func ChainWithTimeout(d time.Duration) ChainOption

ChainWithTimeout sets a timeout for the entire chain.

type Config

type Config struct {
	StackDepth     int  // Maximum stack trace depth; 0 uses default (32).
	ContextSize    int  // Initial context map size; 0 uses default (4).
	DisablePooling bool // If true, disables object pooling for errors.
	FilterInternal bool // If true, filters internal package frames from stack traces.
	AutoFree       bool // If true, automatically returns errors to pool when GC collects them.
}

Config defines the global configuration for the errors package, controlling stack depth, context size, pooling, and frame filtering.

type ConstantBackoff

type ConstantBackoff struct{}

ConstantBackoff provides a fixed delay for each retry attempt.

func (ConstantBackoff) Backoff

func (c ConstantBackoff) Backoff(_ int, baseDelay time.Duration) time.Duration

Backoff returns the base delay regardless of the attempt number. Implements BackoffStrategy with a constant delay.

type Error

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

Error is a custom error type with enhanced features: message, name, stack trace, context, cause, and metadata like code and category. It is thread-safe and supports pooling for performance.

func Convert

func Convert(err error) *Error

Convert transforms any error into an *Error, preserving its message and wrapping it if needed. Returns nil if the input is nil; returns the original if already an *Error. Uses multiple strategies: direct assertion, errors.As, manual unwrapping, and fallback creation.

func Empty

func Empty() *Error

Empty returns a new empty error with no message, name, or stack trace. Useful for incrementally building errors or as a neutral base. Example:

err := errors.Empty().With("key", "value").WithCode(400)

func Err added in v1.2.0

func Err(msg string, err error) *Error

Err creates a new Error with the given message and wraps the provided error as its cause.

func Errorf

func Errorf(format string, args ...interface{}) *Error

func From

func From(err error) *Error

From transforms any error into an *Error, preserving its message and wrapping it if needed. Alias of Convert; returns nil if input is nil, original if already an *Error.

func FromContext

func FromContext(ctx context.Context, err error) *Error

FromContext creates an *Error from a context and an existing error. Enhances the error with context info: timeout status, deadline, or cancellation. Returns nil if input error is nil; does not store context values directly.

func Merge

func Merge(errs ...error) *Error

Merge combines multiple errors into a single *Error. Aggregates messages with "; " separator, merges contexts and stacks; returns nil if no errors provided.

func Named

func Named(name string) *Error

Named creates an error with the specified name and captures a stack trace. The name doubles as the error message if no message is set. Use for errors where type identification and stack context are important. Example:

err := errors.Named("AuthError").WithCode(401)

func New

func New(text string) *Error

New creates a lightweight error with the given message and no stack trace. Optimized for performance; use Trace() for stack traces. Returns a shared empty error for empty messages to reduce allocations. Example:

err := errors.New("invalid input")

func Newf

func Newf(f any, args ...interface{}) *Error

Newf creates a formatted error, supporting the %w verb for wrapping errors. If the format contains exactly one %w verb with a non-nil error argument, the error is wrapped as the cause. The final error message string generated by Error() will be compatible with the output of fmt.Errorf for the same inputs. Does not capture a stack trace by default. Example:

cause := errors.New("db error")
err := errors.Newf("query failed: %w", cause)
// err.Error() will match fmt.Errorf("query failed: %w", cause).Error()
// errors.Unwrap(err) == cause

func Trace

func Trace(text string) *Error

Trace creates an error with the given message and captures a stack trace. Use when debugging context is needed; for performance, prefer New(). Example:

err := errors.Trace("operation failed")

func Tracef

func Tracef(format string, args ...interface{}) *Error

Tracef creates a formatted error with a stack trace. Supports %w for wrapping errors. Example:

err := errors.Tracef("query %s failed: %w", query, cause)

func Transform

func Transform(err error, fn func(*Error)) *Error

Transform applies transformations to an error, returning a new *Error. Creates a new *Error from non-*Error types before applying fn; returns nil if err is nil.

func WithStack

func WithStack(err error) *Error

WithStack converts any error to an *Error and captures a stack trace. Returns nil if input is nil; adds stack to existing *Error or wraps non-*Error types.

func Wrap

func Wrap(err error, wrapper *Error) *Error

Wrap creates a new *Error that wraps another error with additional context. Uses a copy of the provided wrapper *Error; returns nil if err is nil.

func Wrapf

func Wrapf(err error, format string, args ...interface{}) *Error

Wrapf creates a new formatted *Error that wraps another error. Formats the message and sets the cause; returns nil if err is nil.

func (*Error) As

func (e *Error) As(target interface{}) bool

As attempts to assign the error or one in its chain to the target interface. Supports *Error and standard error types, traversing the cause chain. Returns true if successful. Example:

var target *Error
if errors.As(err, &target) {
  fmt.Println(target.Name())
}

func (*Error) Callback

func (e *Error) Callback(fn func()) *Error

Callback sets a function to be called when Error() is invoked. Useful for logging or side effects on error access. Example:

err := errors.New("test").Callback(func() { log.Println("error accessed") })

func (*Error) Category

func (e *Error) Category() string

Category returns the error’s category, if set. Example:

if err.Category() == "network" {
  handleNetworkError(err)
}

func (*Error) Code

func (e *Error) Code() int

Code returns the error’s HTTP-like status code, if set. Returns 0 if no code is set. Example:

if err.Code() == 404 {
  renderNotFound()
}

func (*Error) Context

func (e *Error) Context() map[string]interface{}

Context returns the error’s context as a map, merging smallContext and map-based context. Thread-safe; lazily initializes the map if needed. Example:

ctx := err.Context()
if userID, ok := ctx["user_id"]; ok {
  fmt.Println(userID)
}

func (*Error) Copy

func (e *Error) Copy() *Error

Copy creates a deep copy of the error, preserving all fields except stack freshness. The new error can be modified independently. Example:

newErr := err.Copy().With("new_key", "value")

func (*Error) Count

func (e *Error) Count() uint64

Count returns the number of times the error has been incremented. Useful for tracking error frequency. Example:

fmt.Printf("Error occurred %d times", err.Count())

func (*Error) Err

func (e *Error) Err() error

Err returns the error as an error interface. Useful for type assertions or interface compatibility. Example:

var stdErr error = err.Err()

func (*Error) Error

func (e *Error) Error() string

Error returns the string representation of the error. If the error was created using Newf/Errorf with the %w verb, it returns the pre-formatted string compatible with fmt.Errorf. Otherwise, it combines the message, template, or name with the cause's error string, separated by ": ". Invokes any set callback.

func (*Error) FastStack

func (e *Error) FastStack() []string

FastStack returns a lightweight stack trace with file and line numbers only. Omits function names for performance; skips internal frames if configured. Returns nil if no stack trace exists. Example:

for _, frame := range err.FastStack() {
  fmt.Println(frame) // e.g., "main.go:42"
}

func (*Error) Find

func (e *Error) Find(pred func(error) bool) error

Find searches the error chain for the first error where pred returns true. Returns nil if no match is found or if pred is nil. Example:

err := err.Find(func(e error) bool { return strings.Contains(e.Error(), "timeout") })

func (*Error) Format

func (e *Error) Format() string

Format returns a detailed, human-readable string representation of the error, including message, code, context, stack, and cause. Recursive for causes that are also *Error. Example:

fmt.Println(err.Format())
// Output:
// Error: failed: cause
// Code: 500
// Context:
//   key: value
// Stack:
//   1. main.main main.go:42

func (*Error) Free

func (e *Error) Free()

Free resets the error and returns it to the pool if pooling is enabled. Safe to call multiple times; no-op if pooling is disabled. Call after use to return the error to the pool and prevent memory leaks. Use defer err.Free() at the call site that created the error. Example:

defer err.Free()

func (*Error) Has

func (e *Error) Has() bool

Has checks if the error contains meaningful content (message, template, name, or cause). Returns false for nil or empty errors. Example:

if !err.Has() {
  return nil
}

func (*Error) HasContextKey

func (e *Error) HasContextKey(key string) bool

HasContextKey checks if the specified key exists in the error’s context. Thread-safe; checks both smallContext and map-based context. Example:

if err.HasContextKey("user_id") {
  fmt.Println(err.Context()["user_id"])
}

func (*Error) Increment

func (e *Error) Increment() *Error

Increment atomically increases the error’s count by 1 and returns the error. Useful for tracking repeated occurrences. Example:

err := err.Increment()

func (*Error) Is

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

Is checks if the error matches the target by pointer, name, or cause chain. Compatible with errors.Is; also matches by string for standard errors. Returns true if the error or its cause matches the target. Example:

if errors.Is(err, errors.New("target")) {
  handleTargetError()
}

func (*Error) IsEmpty

func (e *Error) IsEmpty() bool

IsEmpty checks if the error lacks meaningful content (no message, name, template, or cause). Returns true for nil or fully empty errors. Example:

if err.IsEmpty() {
  return nil
}

func (*Error) IsNull

func (e *Error) IsNull() bool

IsNull checks if the error is nil, empty, or contains only SQL NULL values in its context or cause. Useful for handling database-related errors. Example:

if err.IsNull() {
  return nil
}

func (*Error) LogValue added in v1.3.0

func (e *Error) LogValue() slog.Value

LogValue implements slog.LogValuer so *Error can be passed directly to any slog logging call and will be rendered as a structured group containing message, name, code, category, and context fields.

Example:

slog.Error("request failed", "err", err)
// => err.message="...", err.name="AuthError", err.code=401, ...

func (*Error) MarshalJSON

func (e *Error) MarshalJSON() ([]byte, error)

MarshalJSON serializes the error to JSON, including name, message, context, cause, stack, and code. Causes are recursively serialized if they implement json.Marshaler or are *Error. Example:

data, _ := json.Marshal(err)
fmt.Println(string(data))

func (*Error) Msgf

func (e *Error) Msgf(format string, args ...interface{}) *Error

Msgf sets the error’s message using a formatted string and returns the error. Overwrites any existing message. Example:

err := err.Msgf("user %s not found", username)

func (*Error) Name

func (e *Error) Name() string

Name returns the error’s name, if set. Example:

if err.Name() == "AuthError" {
  handleAuthError()
}

func (*Error) Reset

func (e *Error) Reset()

Reset clears all fields of the error, preparing it for reuse in the pool. Internal use by Free; does not release stack to stackPool. Example:

err.Reset() // Clear all fields.

func (*Error) Stack

func (e *Error) Stack() []string

Stack returns a detailed stack trace with function names, files, and line numbers. Filters internal frames if configured; returns nil if no stack exists. Example:

for _, frame := range err.Stack() {
  fmt.Println(frame) // e.g., "main.main main.go:42"
}

func (*Error) Trace

func (e *Error) Trace() *Error

Trace ensures the error has a stack trace, capturing it if absent. Returns the error for chaining. Example:

err := errors.New("failed").Trace()

func (*Error) Transform

func (e *Error) Transform(fn func(*Error)) *Error

Transform applies transformations to a copy of the error and returns the new error. The original error is unchanged; nil-safe. Example:

newErr := err.Transform(func(e *Error) { e.With("key", "value") })

func (*Error) Unwrap

func (e *Error) Unwrap() error

Unwrap returns the underlying cause of the error, if any. Compatible with errors.Unwrap for chain traversal. Example:

cause := errors.Unwrap(err)

func (*Error) UnwrapAll

func (e *Error) UnwrapAll() []error

UnwrapAll returns a slice of all errors in the chain, starting with this error. Each error is isolated to prevent modifications affecting others. Example:

chain := err.UnwrapAll()
for _, e := range chain {
  fmt.Println(e.Error())
}

func (*Error) Walk

func (e *Error) Walk(fn func(error))

Walk traverses the error chain, applying fn to each error. Stops if fn is nil or the chain ends. Example:

err.Walk(func(e error) { fmt.Println(e.Error()) })

func (*Error) With

func (e *Error) With(keyValues ...interface{}) *Error

With adds key-value pairs to the error's context and returns the error. Uses a fixed-size array (smallContext) for up to contextSize items, then switches to a map. Thread-safe. Accepts variadic key-value pairs. Example:

err := err.With("key1", value1, "key2", value2)

func (*Error) WithCategory

func (e *Error) WithCategory(category ErrorCategory) *Error

WithCategory sets the error’s category and returns the error. Example:

err := err.WithCategory("validation")

func (*Error) WithCode

func (e *Error) WithCode(code int) *Error

WithCode sets an HTTP-like status code and returns the error. Example:

err := err.WithCode(400)

func (*Error) WithName

func (e *Error) WithName(name string) *Error

WithName sets the error’s name and returns the error. Example:

err := err.WithName("AuthError")

func (*Error) WithRetryable

func (e *Error) WithRetryable() *Error

WithRetryable marks the error as retryable in its context and returns the error. Example:

err := err.WithRetryable()

func (*Error) WithStack

func (e *Error) WithStack() *Error

WithStack captures a stack trace if none exists and returns the error. Skips one frame (caller of WithStack). Example:

err := errors.New("failed").WithStack()

func (*Error) WithTemplate

func (e *Error) WithTemplate(template string) *Error

WithTemplate sets a message template and returns the error. Used as a fallback if the message is empty. Example:

err := err.WithTemplate("operation failed")

func (*Error) WithTimeout

func (e *Error) WithTimeout() *Error

WithTimeout marks the error as a timeout error in its context and returns the error. Example:

err := err.WithTimeout()

func (*Error) Wrap

func (e *Error) Wrap(cause error) *Error

Wrap associates a cause error with this error, creating a chain. Returns the error unchanged if cause is nil. Example:

err := errors.New("failed").Wrap(errors.New("cause"))

func (*Error) WrapNotNil

func (e *Error) WrapNotNil(cause error) *Error

WrapNotNil wraps a cause error only if it is non-nil and returns the error. Example:

err := err.WrapNotNil(maybeError)

func (*Error) Wrapf

func (e *Error) Wrapf(cause error, format string, args ...interface{}) *Error

Wrapf wraps a cause error with formatted message and returns the error. If cause is nil, returns the error unchanged. Example:

err := errors.New("base").Wrapf(io.EOF, "read failed: %s", "file.txt")

type ErrorCategory

type ErrorCategory string

ErrorCategory is a string type for categorizing errors (e.g., "network", "validation").

type ErrorFormatter

type ErrorFormatter func([]error) string

ErrorFormatter defines a function for custom error message formatting. Takes a slice of errors and returns a single formatted string.

type ErrorOpts

type ErrorOpts struct {
	SkipStack int // Number of stack frames to skip when capturing the stack trace.
}

ErrorOpts provides options for customizing error creation.

type ErrorPool

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

ErrorPool is a high-performance, thread-safe pool for reusing *Error instances. Reduces allocation overhead by recycling errors; tracks hit/miss statistics.

func NewErrorPool

func NewErrorPool() *ErrorPool

NewErrorPool creates a new ErrorPool instance. Initializes the pool with a New function that returns a fresh *Error with default smallContext.

func (*ErrorPool) Get

func (ep *ErrorPool) Get() *Error

Get retrieves an *Error from the pool or creates a new one if pooling is disabled or pool is empty. Resets are handled by Put; thread-safe; updates hit/miss stats when pooling is enabled.

func (*ErrorPool) Put

func (ep *ErrorPool) Put(e *Error)

Put returns an *Error to the pool after resetting it. Ignores nil errors or if pooling is disabled; preserves stack capacity; thread-safe.

func (*ErrorPool) Stats

func (ep *ErrorPool) Stats() (hits, misses int64)

Stats returns the current pool statistics as hits and misses. Thread-safe; uses atomic loads to ensure accurate counts.

type ExponentialBackoff

type ExponentialBackoff struct{}

ExponentialBackoff provides an exponentially increasing delay for retry attempts.

func (ExponentialBackoff) Backoff

func (e ExponentialBackoff) Backoff(attempt int, baseDelay time.Duration) time.Duration

Backoff returns a delay that doubles with each attempt, starting from the base delay. Uses bit shifting for efficient exponential growth (e.g., baseDelay * 2^(attempt-1)).

type Group added in v1.3.0

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

Group runs goroutines concurrently and collects every error they return. The zero value is ready to use; options may be applied via NewGroup.

Example — fan-out with full error collection:

g := errors.NewGroup()
g.Go(func() error { return validateUser(id) })
g.Go(func() error { return validatePerms(id) })
if err := g.Wait(); err != nil {
    // err is *MultiError containing all failures
    log.Println(err)
}

func NewGroup added in v1.3.0

func NewGroup(opts ...GroupOption) *Group

NewGroup creates a Group with the given options applied.

func (*Group) Errors added in v1.3.0

func (g *Group) Errors() []error

Errors returns a snapshot of errors collected so far. Safe to call concurrently with Go/GoCtx; may be incomplete before Wait returns.

func (*Group) Go added in v1.3.0

func (g *Group) Go(fn func() error)

Go starts fn in a new goroutine. Errors returned by fn are collected; nil returns are ignored. Thread-safe: MultiError.Add handles its own locking. cancelOnFirst is read-only after construction so no lock is needed.

func (*Group) GoCtx added in v1.3.0

func (g *Group) GoCtx(fn func(ctx context.Context) error)

GoCtx starts fn in a new goroutine, passing the group's context. If the group was created with GroupWithContext, fn receives a context that is cancelled when cancelOnFirst triggers or the parent is done.

func (*Group) Wait added in v1.3.0

func (g *Group) Wait() error

Wait blocks until all goroutines have finished and returns a *MultiError containing every error collected, or nil if all succeeded. Always returns *MultiError (never collapses to a raw error) so callers can reliably type-assert the result.

type GroupOption added in v1.3.0

type GroupOption func(*Group)

GroupOption configures a Group.

func GroupWithContext added in v1.3.0

func GroupWithContext(ctx context.Context, cancelOnFirst bool) GroupOption

GroupWithContext attaches ctx to the Group. The ctx is passed to context-aware Go calls (GoCtx). If cancelOnFirst is true, the context is cancelled as soon as the first error is returned by any goroutine — useful for "cancel siblings on first failure" patterns.

func GroupWithLimit added in v1.3.0

func GroupWithLimit(n int) GroupOption

GroupWithLimit sets a maximum error limit on the underlying MultiError. Errors beyond the limit are dropped.

type HTTPOption added in v1.3.0

type HTTPOption func(*httpConfig)

HTTPOption configures an HTTPError call.

func WithBody added in v1.3.0

func WithBody(include bool) HTTPOption

WithBody controls whether the error message is written as the response body. Default is true.

func WithBodyFunc added in v1.3.0

func WithBodyFunc(fn func(error) string) HTTPOption

WithBodyFunc sets a custom function that produces the response body string from the error. Overrides WithBody when set.

Example — return JSON instead of plain text:

errors.HTTPError(w, err,
    errors.WithBodyFunc(func(e error) string {
        return fmt.Sprintf(`{"error":%q}`, e.Error())
    }),
)

func WithFallbackCode added in v1.3.0

func WithFallbackCode(code int) HTTPOption

WithFallbackCode sets the HTTP status used when err carries no valid code. Default is 500 (Internal Server Error).

type InspectOption added in v1.3.0

type InspectOption func(*inspectConfig)

InspectOption configures an Inspect call.

func WithMaxDepth added in v1.3.0

func WithMaxDepth(n int) InspectOption

WithMaxDepth sets the maximum chain depth traversed before output is truncated. Default is 10.

func WithStackFrames added in v1.3.0

func WithStackFrames(n int) InspectOption

WithStackFrames sets the maximum number of stack frames printed per error node. Default is 3.

type LinearBackoff

type LinearBackoff struct{}

LinearBackoff provides a linearly increasing delay for retry attempts.

func (LinearBackoff) Backoff

func (l LinearBackoff) Backoff(attempt int, baseDelay time.Duration) time.Duration

Backoff returns a delay that increases linearly with each attempt (e.g., baseDelay * attempt). Implements BackoffStrategy with linear progression.

type MultiError

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

MultiError represents a thread-safe collection of errors with enhanced features. Supports limits, sampling, and custom formatting for error aggregation.

func NewMultiError

func NewMultiError(opts ...MultiErrorOption) *MultiError

NewMultiError creates a new MultiError instance with optional configuration. Initial capacity is set to 4; applies options in the order provided.

func (*MultiError) Add

func (m *MultiError) Add(errs ...error)

Add appends an error to the collection with optional sampling, limit checks, and duplicate prevention. Ignores nil errors and duplicates based on string equality; thread-safe.

func (*MultiError) Addf

func (m *MultiError) Addf(format string, args ...interface{})

Addf formats and adds a new error to the collection.

func (*MultiError) Clear

func (m *MultiError) Clear()

Clear removes all errors from the collection. Thread-safe; resets the slice while preserving capacity.

func (*MultiError) Count

func (m *MultiError) Count() int

Count returns the number of errors in the collection. Thread-safe.

func (*MultiError) Error

func (m *MultiError) Error() string

Error returns a formatted string representation of the errors. Returns empty string if no errors, single error message if one exists, or a formatted list using custom formatter or default if multiple; thread-safe.

func (*MultiError) Errors

func (m *MultiError) Errors() []error

Errors returns a copy of the contained errors. Thread-safe; returns nil if no errors exist.

func (*MultiError) Filter

func (m *MultiError) Filter(fn func(error) bool) *MultiError

Filter returns a new MultiError containing only errors that match the predicate. Thread-safe; preserves original configuration including limit, formatter, and sampling.

func (*MultiError) First

func (m *MultiError) First() error

First returns the first error in the collection, if any. Thread-safe; returns nil if the collection is empty.

func (*MultiError) Has

func (m *MultiError) Has() bool

Has reports whether the collection contains any errors. Thread-safe.

func (*MultiError) IsNull

func (m *MultiError) IsNull() bool

IsNull checks if the MultiError is empty or contains only null errors. Returns true if empty or all errors are null (via IsNull() or empty message); thread-safe.

func (*MultiError) Last

func (m *MultiError) Last() error

Last returns the most recently added error in the collection, if any. Thread-safe; returns nil if the collection is empty.

func (*MultiError) MarshalJSON

func (m *MultiError) MarshalJSON() ([]byte, error)

MarshalJSON serializes the MultiError to JSON, including all contained errors and configuration metadata. Thread-safe; errors are serialized using their MarshalJSON method if available, otherwise as strings.

func (*MultiError) Merge

func (m *MultiError) Merge(other *MultiError)

Merge combines another MultiError's errors into this one. Thread-safe; respects this instance’s limit and sampling settings; no-op if other is nil or empty.

func (*MultiError) Single

func (m *MultiError) Single() error

Single returns nil if the collection is empty, the single error if only one exists, or the MultiError itself if multiple errors are present. Thread-safe; useful for unwrapping to a single error when possible.

func (*MultiError) String

func (m *MultiError) String() string

String implements the Stringer interface for a concise string representation. Thread-safe; delegates to Error() for formatting.

func (*MultiError) Unwrap

func (m *MultiError) Unwrap() []error

Unwrap returns a copy of the contained errors for multi-error unwrapping. Implements the errors.Unwrap interface; thread-safe; returns nil if empty.

type MultiErrorOption

type MultiErrorOption func(*MultiError)

MultiErrorOption configures MultiError behavior during creation.

func WithFormatter

func WithFormatter(f ErrorFormatter) MultiErrorOption

WithFormatter sets a custom error formatting function. Returns a MultiErrorOption for use with NewMultiError; overrides default formatting.

func WithLimit

func WithLimit(n int) MultiErrorOption

WithLimit sets the maximum number of errors to store. Returns a MultiErrorOption for use with NewMultiError; 0 means unlimited, negative values are ignored.

func WithRand

func WithRand(r *rand.Rand) MultiErrorOption

WithRand sets a custom random source for sampling, useful for testing. Returns a MultiErrorOption for use with NewMultiError; defaults to fastRand if nil.

func WithSampling

func WithSampling(rate uint32) MultiErrorOption

WithSampling enables error sampling with a specified rate (1-100). Returns a MultiErrorOption for use with NewMultiError; caps rate at 100 for validity.

type Retry

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

Retry represents a retryable operation with configurable backoff and retry logic. Supports multiple attempts, delay strategies, jitter, and context-aware cancellation.

func NewRetry

func NewRetry(options ...RetryOption) *Retry

NewRetry creates a new Retry instance with the given options. Defaults: 3 attempts, 100ms base delay, 10s max delay, exponential backoff with jitter, and retrying on IsRetryable errors; ensures retryIf is never nil.

func (*Retry) Attempts

func (r *Retry) Attempts() int

Attempts returns the configured maximum number of retry attempts. Includes the initial attempt in the count.

func (*Retry) Execute

func (r *Retry) Execute(fn func() error) error

Execute runs the provided function with the configured retry logic. Returns nil on success or the last error if all attempts fail; respects context cancellation.

func (*Retry) ExecuteContext

func (r *Retry) ExecuteContext(ctx context.Context, fn func() error) error

ExecuteContext runs the provided function with retry logic, respecting context cancellation. Returns nil on success or the last error if all attempts fail or context is cancelled.

func (*Retry) Transform

func (r *Retry) Transform(opts ...RetryOption) *Retry

Transform creates a new Retry instance with modified configuration. Copies all settings from the original Retry and applies the given options.

type RetryOption

type RetryOption func(*Retry)

RetryOption configures a Retry instance. Defines a function type for setting retry parameters.

func WithBackoff

func WithBackoff(strategy BackoffStrategy) RetryOption

WithBackoff sets the backoff strategy using the BackoffStrategy interface. Returns a RetryOption; no-op if strategy is nil, retaining the existing strategy.

func WithContext

func WithContext(ctx context.Context) RetryOption

WithContext sets the context for cancellation and deadlines. Returns a RetryOption; retains context.Background if ctx is nil.

func WithDelay

func WithDelay(delay time.Duration) RetryOption

WithDelay sets the initial delay between retries. Returns a RetryOption; ensures non-negative delay by setting negatives to 0.

func WithJitter

func WithJitter(jitter bool) RetryOption

WithJitter enables or disables jitter in the backoff delay. Returns a RetryOption; toggles random delay variation.

func WithMaxAttempts

func WithMaxAttempts(maxAttempts int) RetryOption

WithMaxAttempts sets the maximum number of retry attempts. Returns a RetryOption; ensures at least 1 attempt by adjusting lower values.

func WithMaxDelay

func WithMaxDelay(maxDelay time.Duration) RetryOption

WithMaxDelay sets the maximum delay between retries. Returns a RetryOption; ensures non-negative delay by setting negatives to 0.

func WithOnRetry

func WithOnRetry(onRetry func(attempt int, err error)) RetryOption

WithOnRetry sets a callback to execute after each failed attempt. Returns a RetryOption; callback receives attempt number and error.

func WithRetryIf

func WithRetryIf(retryIf func(error) bool) RetryOption

WithRetryIf sets the condition under which to retry. Returns a RetryOption; retains IsRetryable default if retryIf is nil.

type Sentinel added in v1.3.0

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

Sentinel is a comparable, immutable error value safe to store as a package-level variable and match with errors.Is or a type switch.

Unlike Named(), which returns a new *Error instance on every call (making pointer equality unreliable), each call to Const() returns a unique stable pointer. Two sentinels with identical name/msg are still distinct values unless they are the same pointer — intentional, to avoid accidental aliasing.

func Const added in v1.3.0

func Const(name, msg string) *Sentinel

Const creates a new sentinel error with the given name and message. Store the result as a package-level var; never call Const in a hot path.

Example:

var (
    ErrNotFound   = errors.Const("not_found",   "resource not found")
    ErrForbidden  = errors.Const("forbidden",   "access denied")
    ErrBadRequest = errors.Const("bad_request", "invalid input")
)

func handle(err error) {
    switch {
    case errors.Is(err, ErrNotFound):   // 404
    case errors.Is(err, ErrForbidden):  // 403
    }
}

func (*Sentinel) As added in v1.3.0

func (s *Sentinel) As(target any) bool

As attempts to assign the sentinel to target if target is **Sentinel. Returns true if the assignment was made.

func (*Sentinel) Error added in v1.3.0

func (s *Sentinel) Error() string

Error implements the error interface.

func (*Sentinel) Is added in v1.3.0

func (s *Sentinel) Is(target error) bool

Is reports whether target is the same sentinel (pointer equality). This satisfies the errors.Is contract.

func (*Sentinel) LogValue added in v1.3.0

func (s *Sentinel) LogValue() slog.Value

LogValue implements slog.LogValuer so a Sentinel can be passed directly to any slog logging call and rendered as a structured group.

Example:

slog.Error("lookup failed", "err", ErrNotFound)
// => err.error="resource not found", err.code="not_found"

func (*Sentinel) MarshalJSON added in v1.3.0

func (s *Sentinel) MarshalJSON() ([]byte, error)

MarshalJSON serialises the sentinel to {"error":"...","code":"..."}.

func (*Sentinel) Name added in v1.3.0

func (s *Sentinel) Name() string

Name returns the sentinel's name, useful for logging and diagnostics.

func (*Sentinel) String added in v1.3.0

func (s *Sentinel) String() string

String returns a debug-friendly representation.

func (*Sentinel) Unwrap added in v1.3.0

func (s *Sentinel) Unwrap() error

Unwrap returns nil — sentinels are root errors with no cause chain. Satisfies the errors.Unwrap contract.

func (*Sentinel) With added in v1.3.0

func (s *Sentinel) With(msg string) *Error

With returns a new *Error that wraps this sentinel as its cause and carries the additional message msg. Use this to add call-site context to a sentinel without losing the ability to match the original with errors.Is.

Example:

var ErrNotFound = errors.Const("not_found", "resource not found")

// At call site:
err := ErrNotFound.With("user 42 not found")
errors.Is(err, ErrNotFound) // true — sentinel is in the cause chain

type Stream added in v1.3.0

type Stream[T any] struct {
	// contains filtered or unexported fields
}

Stream processes a slice of items concurrently, collecting errors as they occur without stopping execution. Use Wait() to block until all items are done, or Each() to process errors as they arrive.

Example — collect all:

s := errors.NewStream(ctx, items, process, 8)
if err := s.Wait(); err != nil {
    log.Println(err)
}

Example — process as they arrive:

s := errors.NewStream(ctx, items, process, 8)
s.Each(func(err error) { log.Println(err) })

func NewStream added in v1.3.0

func NewStream[T any](ctx context.Context, items []T, fn func(T) error, workers ...int) *Stream[T]

NewStream creates a Stream that applies fn to every item in items using up to workers concurrent goroutines.

workers <= 0 defaults to len(items), running all items at once. Respects ctx: in-flight work completes but no new items start once ctx is done.

Example:

s := errors.NewStream(ctx, urls, func(url string) error {
    return fetch(url)
}, 8)

func (*Stream[T]) Each added in v1.3.0

func (s *Stream[T]) Each(fn func(error))

Each calls fn for every error produced by the stream, in the order they arrive. Blocks until all items have been processed.

Panics if called after Wait (or a second call to Each).

func (*Stream[T]) Stop added in v1.3.0

func (s *Stream[T]) Stop()

Stop signals the stream to stop processing new items and drains any buffered errors in the background. Safe to call multiple times. After Stop, Wait and Each will still return promptly but may not see all errors.

func (*Stream[T]) Wait added in v1.3.0

func (s *Stream[T]) Wait() error

Wait blocks until all items have been processed and returns a *MultiError containing every error collected, or nil if all items succeeded.

Panics if called after Each (or a second call to Wait).

Directories

Path Synopsis
Package main demonstrates basic usage of the errors package from github.com/olekukonko/errors.
Package main demonstrates basic usage of the errors package from github.com/olekukonko/errors.
Package errmgr provides common error definitions and categories for use across applications.
Package errmgr provides common error definitions and categories for use across applications.

Jump to

Keyboard shortcuts

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