trace

package module
v2.0.0 Latest Latest
Warning

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

Go to latest
Published: Aug 16, 2026 License: Apache-2.0 Imports: 15 Imported by: 0

README

Trace - Modern Go Error Handling

Go Reference Go Report Card

A modern error handling package for Go, inspired by gravitational/trace but upgraded for Go 1.25+ with:

  • 🔍 Stack traces - Know exactly where errors originate
  • 🏷️ Typed errors - NotFound, BadParameter, AccessDenied, etc.
  • 📊 slog integration - Structured logging out of the box
  • 🔗 Full errors.Is/As support - Compatible with Go 1.13+ error handling
  • 🧬 Generics - Result types, pipelines, and type-safe operations
  • 🌐 HTTP utilities - Middleware, error responses, status code mapping
  • 📦 Context integration - Trace IDs, fields, cancel cause, detached contexts
  • 🔄 Error chain iterator - for e := range trace.Errors(err) (Go 1.23+)
  • 🧰 System error conversion - os, io/fs, and syscall failures become typed errors

Installation

go get github.com/tae2089/trace/v2

Packages

Package Import path Contents
trace github.com/tae2089/trace/v2 Error values, stack frames, typed categories, context helpers, slog integration, Result/Pipeline
tracehttp github.com/tae2089/trace/v2/tracehttp Everything that touches net/http: status mapping, JSON error responses, middleware, client

The split exists so that programs which only need error values never pay for net/http. Measured on Go 1.25:

Dependency graph stdlib packages
trace 72
trace + tracehttp 185

Importing net/http also drags in the whole crypto/tls and crypto/x509 tree. CI fails the build if net/http reappears in the root package's graph.

Quick Start

package main

import (
    "fmt"

    "github.com/tae2089/trace/v2"
)

func main() {
    err := fetchUser("user-123")
    if err != nil {
        if trace.IsNotFound(err) {
            fmt.Println("User not found")
        }
        // Full debug output with stack trace
        fmt.Printf("%+v\n", err)
    }
}

func fetchUser(id string) error {
    _, err := queryDatabase(id)
    if err != nil {
        return trace.Wrapf(err, "failed to fetch user %s", id)
    }
    return nil
}

type User struct{}

func queryDatabase(id string) (*User, error) {
    // Simulate not found
    return nil, trace.NotFound("user %s does not exist", id)
}

Output:

User not found
[main.go:25 <- main.go:18 <- main.go:12] failed to fetch user user-123
→ user user-123 does not exist
Stack trace:
  main.go:25 main.queryDatabase
  main.go:18 main.fetchUser
  main.go:12 main.main

Core Features

Basic Wrapping
// Simple wrap - adds stack frame
err := trace.Wrap(originalErr)

// Wrap with message
err := trace.Wrap(originalErr, "operation failed")

// Wrap with formatted message
err := trace.Wrapf(originalErr, "failed to process user %s", userID)

// Create new error with stack trace
err := trace.New("something went wrong")
err := trace.Errorf("failed to process %d items", count)
Typed Errors
// Create typed errors
err := trace.NotFound("user %s not found", userID)
err := trace.AlreadyExists("email already registered")
err := trace.BadParameter("invalid email format")
err := trace.Unauthenticated("invalid bearer token")
err := trace.AccessDenied("insufficient permissions")
err := trace.Conflict("version mismatch")
err := trace.LimitExceeded("rate limit exceeded")
err := trace.Timeout(originalErr, "request timed out")
err := trace.ConnectionProblem(originalErr, "database unreachable")
err := trace.NotImplemented("feature coming soon")

// Wrap existing error as typed
err := trace.WrapNotFound(sql.ErrNoRows, "user not found")
err := trace.WrapAccessDenied(err, "permission check failed")

// Check error types (works through wrapped errors)
if trace.IsNotFound(err) { /* handle 404 */ }
if trace.IsUnauthenticated(err) { /* handle 401 */ }
if trace.IsAccessDenied(err) { /* handle 403 */ }
if trace.IsRetryable(err) { /* retry the operation */ }

// Get HTTP status code
statusCode := tracehttp.GetHTTPStatusCode(err) // e.g., 404, 403, 500
Structured Fields
// Add fields for structured logging
err := trace.NotFound("user not found")
err = trace.WithField(err, "user_id", userID)
err = trace.WithFields(err, map[string]any{
    "request_id": reqID,
    "tenant":     tenant,
})

// Or create with fields directly
err := trace.WrapWithFields(originalErr, map[string]any{
    "user_id": userID,
    "action":  "delete",
}, "operation failed")

// Extract fields
fields := trace.GetFields(err)
slog Integration
import "log/slog"

logger := slog.Default()

// Log with full trace information
err := trace.Wrap(dbErr, "query failed")
logger.Error("operation failed", trace.SlogError(err))

// Output (JSON):
// {
//   "level": "ERROR",
//   "msg": "operation failed",
//   "error": {
//     "message": "query failed",
//     "cause": "connection refused",
//     "trace": [
//       {"file": "repo.go", "line": 42, "func": "repo.Query"},
//       {"file": "service.go", "line": 28, "func": "service.GetUser"}
//     ]
//   }
// }

// Use the error handler for automatic extraction
handler := trace.NewErrorHandler(slog.NewJSONHandler(os.Stdout, nil))
logger := slog.New(handler)

// Passing nil uses slog.DiscardHandler — safe for tests or optional logging
discardHandler := trace.NewErrorHandler(nil)
HTTP Utilities
// The application owns request logging; trace only classifies and renders.
func Handle(fn tracehttp.ErrorHandlerFunc) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        if err := fn(w, r); err != nil {
            httpErr := tracehttp.ToHTTPError(err)
            logger.Error("request failed",
                trace.SlogError(err),
                slog.Int("status_code", httpErr.Status),
                slog.String("method", r.Method),
                slog.String("path", r.URL.Path),
            )

            requestID := r.Header.Get("X-Request-ID")
            if writeErr := tracehttp.WriteError(w, err, requestID); writeErr != nil {
                logger.Error("failed to write error response", "error", writeErr)
            }
        }
    }
}

http.Handle("/users/{id}", Handle(getUserHandler))

The response contains only stable client-safe fields:

{
  "error": {
    "code": "not_found",
    "message": "user not found",
    "request_id": "01KREQUEST"
  }
}

Framework adapters such as Gin can use ErrorResponseFor without writing through net/http:

status, response := tracehttp.ErrorResponseFor(err, requestID)

request_id is supplied explicitly and is separate from internal trace_id fields. Error fields, details, causes, stack frames, and outer trace.Wrap messages are never copied into the response. All 5xx messages are normalized to internal server error.

WriteErrorWithLogger, ErrorMiddlewareWithLogger, and RecoverMiddleware were removed in v2. Applications should decide logging level, duration, route, response-size, and panic-recovery policy themselves.

Which error decides the response

ToHTTPError walks the chain from the outside in and stops at the first link it can classify, so an outer wrap always beats an inner one:

err := trace.NotFound("secret project not found")
err = trace.WrapAccessDenied(err, "not a member")

tracehttp.ToHTTPError(err) // 403 access_denied — not 404

That ordering matters for more than tidiness. If the inner NotFound won, the response would tell an unauthorized caller that the resource exists.

An application type that implements HTTPErrorProvider joins the same walk and wins at whatever depth it sits:

type QuotaError struct{ Plan string }

func (e *QuotaError) Error() string { return "plan quota reached" }

func (e *QuotaError) HTTPError() tracehttp.HTTPError {
    return tracehttp.HTTPError{
        Status:  http.StatusPaymentRequired,
        Code:    "quota_reached",
        Message: "upgrade required",
    }
}

Statuses outside 400–599, or a blank Code, are rejected and become a generic internal error, and every 5xx message is replaced with internal server error.

Clients using this response contract can safely restore built-in typed errors:

resp, err := http.Get("https://api.example.com/users/123")
if err != nil {
    return err
}
defer resp.Body.Close()

body, err := io.ReadAll(resp.Body)
if err != nil {
    return err
}
if err := tracehttp.ReadErrorResponse(resp.StatusCode, body); err != nil {
    if trace.IsNotFound(err) {
        // error.code was "not_found"
    }
    return err
}

ReadErrorResponse reads only error.code, error.message, and request_id from the public envelope. It creates a new local trace and never deserializes remote causes, fields, details, frames, or an internal TraceError. The HTTP status must agree with the code; malformed envelopes, unknown/custom codes, and status/code mismatches become a generic internal error without retaining the raw response body. Authentication, authorization, cancellation, and all 5xx messages are normalized again while decoding.

error.code HTTP status Restored predicate
bad_request 400 IsBadParameter
unauthenticated 401 IsUnauthenticated
access_denied 403 IsAccessDenied
not_found 404 IsNotFound
already_exists 409 IsAlreadyExists
conflict 409 IsConflict
limit_exceeded 429 IsLimitExceeded
canceled 499 IsCanceled
not_implemented 501 IsNotImplemented
unavailable 503 IsConnectionProblem
timeout 504 IsTimeout
internal 5xx Generic internal error

FromHTTPResponse remains available for legacy status/plain-text responses. It classifies by status and retains the response body in the developer-facing error, so prefer ReadErrorResponse for the safe JSON contract above.

Context Integration
// Add trace ID and fields to context
ctx := trace.ContextWithTraceID(ctx, "req-abc-123")
ctx = trace.ContextWithField(ctx, "user_id", userID)

// Wrap errors with context information
err := trace.WrapContext(ctx, dbErr, "query failed")
// err now contains trace_id and user_id in fields

// Check context errors — FromContext uses context.Cause to preserve the
// original cancellation reason (e.g., the error passed to CancelCauseFunc)
if err := trace.FromContext(ctx); err != nil {
    if trace.IsCanceled(err) {
        // Handle cancellation
    }
    if trace.IsTimeout(err) {
        // Handle timeout
    }
}

// Cancel with cause — the cause is preserved through FromContext
ctx, cancel := trace.WithCancelCause(parentCtx)
cancel(errors.New("shutdown requested"))
err := trace.FromContext(ctx) // inner error is "shutdown requested", not generic "context canceled"

// Timeout with cause
ctx, cancel := trace.WithTimeoutCause(parentCtx, 5*time.Second, errors.New("slow query"))
defer cancel()
// if timeout fires, FromContext(ctx) wraps "slow query" as a TimeoutError

// Detached context — carries values but not cancellation
// Useful for background goroutines that should outlive the request
detached := trace.DetachedContext(ctx)
go cleanup(detached) // won't be canceled when parent ctx is canceled

// Cancel callback — run a function when context is canceled
c := trace.NewContextualizer(ctx)
stop := c.OnCancel(func() {
    releaseResource()
})
// call stop() to prevent the callback if no longer needed
Generic Result Type
// Create results
result := trace.Ok(user)
result := trace.Err[*User](errors.New("not found"))

// Use Try for (value, error) functions
result := trace.Try(db.QueryUser(id))

// Check and unwrap
if result.IsOk() {
    user := result.Unwrap()
}

// Safe unwrap with default
user := result.UnwrapOr(defaultUser)

// Transform
nameResult := trace.Map(userResult, func(u *User) string {
    return u.Name
})

// Chain operations
result := trace.FlatMap(userResult, func(u *User) trace.Result[*Profile] {
    return trace.Try(db.GetProfile(u.ID))
})

// Collect multiple results
results := trace.Collect(result1, result2, result3)
if results.IsErr() {
    // Handle aggregated errors
}
Pipeline Pattern
// Chain operations with automatic error propagation
result, err := trace.NewPipeline(userInput).
    Then(validate).
    Then(normalize).
    Then(save).
    Result()

// With recovery
result, err := trace.NewPipeline(data).
    Then(process).
    Recover(func(err error) (Data, error) {
        if trace.IsNotFound(err) {
            return defaultData, nil
        }
        return Data{}, err
    }).
    Result()

// Transform between types
pipeline := trace.NewPipeline(userID)
result := trace.TransformPipeline(pipeline, func(id string) (*User, error) {
    return db.FindUser(id)
})
Aggregate Errors (Go 1.20+)
// Combine multiple errors
errs := []error{err1, err2, err3}
combined := trace.Aggregate(errs...)

// Works with errors.Is/As
if trace.IsNotFound(combined) { /* at least one is NotFound */ }

// Get most severe HTTP status
statusCode := tracehttp.GetHTTPStatusCode(combined)
Error Chain Iterator (Go 1.23+)
// Iterate over the entire error chain using range-over-func
err := trace.Wrap(trace.Wrap(dbErr, "repo"), "service")

for e := range trace.Errors(err) {
    fmt.Println(e)
}

// Works with AggregateError — traverses all branches
agg := trace.Aggregate(err1, err2, err3)
for e := range trace.Errors(agg) {
    if trace.IsNotFound(e) {
        // found a NotFound somewhere in the tree
    }
}
System Error Conversion

ConvertSystemError turns os, io/fs, and syscall failures into typed trace errors so the rest of your code can use trace.IsNotFound and friends instead of matching sentinel values by hand.

f, err := os.Open(path)
if err != nil {
    return trace.ConvertSystemError(err) // fs.ErrNotExist becomes a NotFoundError
}
Source error Result
fs.ErrNotExist NotFoundError
fs.ErrExist AlreadyExistsError
fs.ErrPermission AccessDeniedError
context.Canceled CanceledError
context.DeadlineExceeded, os.ErrDeadlineExceeded, Timeout() bool reporting true TimeoutError
ECONNREFUSED, ECONNRESET, ECONNABORTED, EHOSTUNREACH, ENETUNREACH, ENETDOWN, EPIPE ConnectionProblemError
ETIMEDOUT TimeoutError
EMFILE, ENFILE LimitExceededError
Temporary() bool reporting true ConnectionProblemError

Rules:

  • nil returns nil, and an error that already carries a trace category is returned unchanged — an outer WrapAccessDenied is never downgraded by an inner fs.ErrNotExist.
  • Anything unrecognized is returned as-is, not forced into a category.
  • The conversion attaches no message. Operating system text often contains a file path, so it stays in the cause, where %+v and logs can see it, and out of tracehttp's client response.
  • Errno matching is compiled only on unix and windows; other platforms fall back to the portable rules above and still build.
Debug Output
// Simple error string
fmt.Println(err)
// [repo.go:42 <- service.go:28] failed to fetch user
// → connection refused

// Verbose with full stack trace
fmt.Printf("%+v\n", err)
// [repo.go:42 <- service.go:28] failed to fetch user
// → connection refused
// Stack trace:
//   repo.go:42 repo.Query
//   service.go:28 service.GetUser
//   handler.go:15 handler.HandleRequest
// Fields:
//   user_id: abc123
//   request_id: req-456

// Full debug report
fmt.Println(trace.DebugReport(err))

// User-friendly message (without stack traces)
msg := trace.UserMessage(err) // "failed to fetch user"

Performance

Construction is the hot path — errors are created far more often than they are printed — so v2 moves cost from creation to rendering:

  • A Frame records only the program counter. The function name, file, and line resolve when the error is rendered (Error(), %+v, slog, JSON).
  • The structured fields map is allocated only when a field is attached.
  • Hot paths walk the error chain with plain type assertions instead of errors.As, with the same semantics (including the As(any) bool hook and aggregate branches).

Measured on Apple M1 Pro, Go 1.25 (go test -bench . -benchmem):

Benchmark v1 layout v2
Wrap (1 level) 435 ns, 416 B, 6 allocs 162 ns, 72 B, 2 allocs
NotFound 389 ns, 416 B, 6 allocs 165 ns, 80 B, 3 allocs
Wrap ×10 deep 4.9 µs, 6.4 KB, 69 allocs 1.9 µs, 1.2 KB, 29 allocs
CaptureFrame 285 ns, 248 B, 2 allocs 95 ns, 0 B, 0 allocs
IsNotFound (10 deep) 638 ns 102 ns
err.Error() (5 deep) 1.3 µs 4.4 µs

The last row is the deliberate trade: rendering pays for the deferred symbol resolution. fmt.Errorf("%w") is still ~2× faster than Wrap — that is the price of carrying a stack trace at all.

Best Practices

1. Wrap at Every Layer
// Repository
func (r *UserRepo) FindByID(id string) (*User, error) {
    user, err := r.db.Query(...)
    if err != nil {
        if err == sql.ErrNoRows {
            return nil, trace.WrapNotFound(err, "user %s not found", id)
        }
        return nil, trace.Wrap(err, "database query failed")
    }
    return user, nil
}

// Service
func (s *UserService) GetUser(id string) (*User, error) {
    user, err := s.repo.FindByID(id)
    if err != nil {
        return nil, trace.Wrap(err, "service: get user")
    }
    return user, nil
}

// Handler
func (h *Handler) GetUser(w http.ResponseWriter, r *http.Request) error {
    user, err := h.service.GetUser(r.PathValue("id"))
    if err != nil {
        return trace.Wrap(err) // Final wrap before response
    }
    return json.NewEncoder(w).Encode(user)
}
2. Use Typed Errors for Business Logic
func (s *OrderService) PlaceOrder(ctx context.Context, order Order) error {
    // Check inventory
    if !s.inventory.HasStock(order.ItemID) {
        return trace.Conflict("item %s out of stock", order.ItemID)
    }

    // Check user permissions
    if !s.auth.CanPurchase(ctx, order.UserID) {
        return trace.AccessDenied("user cannot place orders")
    }

    // Validate
    if order.Quantity <= 0 {
        return trace.BadParameter("quantity must be positive")
    }

    return s.repo.SaveOrder(order)
}
3. Add Context for Debugging
func ProcessBatch(ctx context.Context, items []Item) error {
    for i, item := range items {
        if err := processItem(ctx, item); err != nil {
            return trace.WrapWithFields(err, map[string]any{
                "batch_index": i,
                "item_id":     item.ID,
            }, "batch processing failed")
        }
    }
    return nil
}
4. Use Context for Request Tracing
func middleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        ctx := r.Context()
        ctx = trace.ContextWithTraceID(ctx, generateTraceID())
        ctx = trace.ContextWithFields(ctx, map[string]any{
            "method": r.Method,
            "path":   r.URL.Path,
        })
        next.ServeHTTP(w, r.WithContext(ctx))
    })
}

Migration from gravitational/trace

This package is inspired by gravitational/trace, but it is not a drop-in replacement. Important differences:

gravitational/trace This package
trace.Traces embed Use *TraceError directly
trace.OrigError() Use errors.Unwrap() or errors.Is/As
Manual SetTrace Automatic via Wrap()
WriteError serializes trace internals WriteError emits only the public ErrorResponse envelope
ReadError deserializes remote trace internals ReadErrorResponse creates a new local typed error from the public code
Status-driven HTTP reconstruction Code + status validation distinguishes categories sharing a status
Concrete type assertions Behavior interfaces (ErrorNotFound, HTTPErrorProvider) that your own types can implement
gravitational_trace.nocrypto build tag drops net/http Separate tracehttp package; the root package never imports net/http
ConvertSystemError ConvertSystemError
CompareFailed, OAuth2, Trust, Retry No equivalent
No equivalent slog, generics, pipelines, and context integration

Migration from v1

v2 moves every net/http-dependent symbol into tracehttp and removes the deprecated logger-coupled helpers. There are no compatibility shims in the root package, because re-exporting the HTTP helpers would pull net/http back in and undo the reason for the split.

import (
    "github.com/tae2089/trace/v2"
    "github.com/tae2089/trace/v2/tracehttp"
)
v1 v2
trace.ToHTTPError tracehttp.ToHTTPError
trace.ErrorResponseFor tracehttp.ErrorResponseFor
trace.WriteError tracehttp.WriteError
trace.ErrorMiddleware tracehttp.ErrorMiddleware
trace.ReadErrorResponse tracehttp.ReadErrorResponse
trace.FromHTTPResponse tracehttp.FromHTTPResponse
trace.GetHTTPStatusCode tracehttp.GetHTTPStatusCode
trace.IsHTTPError, trace.WrapHTTPError tracehttp.IsHTTPError, tracehttp.WrapHTTPError
trace.NewClient tracehttp.NewClient
trace.HTTPError, trace.ErrorCode, trace.Code* tracehttp.HTTPError, tracehttp.ErrorCode, tracehttp.Code*
trace.HTTPStatusCode interface Removed — implement tracehttp.HTTPErrorProvider instead
err.HTTPStatusCode() / err.HTTPError() methods Removed — call tracehttp.ToHTTPError(err)
trace.WriteErrorWithLogger Removed
trace.ErrorMiddlewareWithLogger Removed
trace.RecoverMiddleware Removed

trace.WithField and trace.WithFields rebuild an error chain when they replace the inner *TraceError. Built-in wrapper types and tracehttp.WrapHTTPError's status override survive this. A third-party wrapper type is dropped unless it implements trace.TraceErrorReplacer — one method, ReplaceTraceError(original, replacement *TraceError) (error, bool), that rebuilds the wrapper around the replacement.

Requirements

  • Go 1.25+ (module requirement in go.mod)
  • Key feature minimum versions:
    • iter.Seq / for range N: Go 1.23+
    • context.WithoutCancel, context.AfterFunc, context.WithTimeoutCause: Go 1.21+
    • slog.DiscardHandler: Go 1.24+
    • log/slog: Go 1.21+
    • errors.Join (Aggregate): Go 1.20+
    • Generics: Go 1.18+

Changelog

v2.0.0
  • Breaking: Module path is now github.com/tae2089/trace/v2
  • Breaking: Every net/http-dependent symbol moved to the tracehttp package
  • Breaking: HTTPStatusCode() and HTTPError() methods removed from the typed errors; tracehttp classifies through the behavior interfaces instead
  • Breaking: HTTPStatusCode interface removed; HTTPErrorProvider is the single extension point
  • Breaking: WriteErrorWithLogger, ErrorMiddlewareWithLogger, and RecoverMiddleware removed
  • Added: ConvertSystemError(err) for os, io/fs, and syscall failures
  • Added: Canceled(err, msg) and WrapLimitExceeded(err, msg) constructors
  • Added: CaptureFrame(skip) is now exported so other packages can build trace errors
  • Added: TraceErrorReplacer hook so wrapper types outside the package survive WithField/WithFields; tracehttp.WrapHTTPError's status override now survives them (it was silently dropped in v1)
  • Breaking: Frame stores only a program counter; Function, File, and Line are methods now, and symbol resolution happens at render time. MarshalJSON keeps the {"function","file","line"} wire shape
  • Performance: Wrap 435→162 ns and 6→2 allocs; a 10-deep wrap chain 4.9µs→1.9µs; IsNotFound on a 10-deep chain 638→102 ns; the structured fields map is allocated lazily and errors.As was replaced with a reflection-free chain walk on hot paths
  • Added: Apache-2.0 LICENSE and a CI workflow that rejects net/http in the root package
  • Changed: ToHTTPError documents and tests outermost-wins classification ordering
v1.2.0
  • Added: Safe HTTPError, ErrorResponseFor, and WriteError response contract
  • Added: ReadErrorResponse(statusCode, body) for safe code-based typed error restoration
  • Changed: Authentication and authorization now use distinct 401/403 categories and stable codes
v1.1.0
  • Added: Errors(err) iter.Seq[error] — range-based error chain iterator (Go 1.23+)
  • Added: DetachedContext(ctx)context.WithoutCancel wrapper for cancel-free child contexts
  • Added: Contextualizer.OnCancel(fn)context.AfterFunc wrapper for cancel callbacks
  • Added: WithCancelCause(ctx) / WithTimeoutCause(ctx, d, cause) — context cause wrappers
  • Changed: FromContext now uses context.Cause to preserve the original cancellation reason
  • Changed: NewErrorHandler(nil) uses slog.DiscardHandler instead of panicking
  • Changed: Benchmarks use for range N (Go 1.22+)
v1.0.2 (2026-01-25)
  • Changed: Error message formatting now uses \n→ separator for better readability of error chains.
v1.0.0
  • Initial release with core error handling features

License

Apache-2.0. See LICENSE.

Documentation

Overview

@index Context helpers for propagating trace IDs, fields, and cancellation causes into trace errors.

@index Typed error categories and retryability rules for trace errors.

@index Generic Result and Pipeline helpers for composing trace-aware success and failure flows.

@index slog integration for serializing trace errors and automatically expanding error attributes in log records.

@index Translation of operating system, filesystem, and network errors into typed trace categories.

@index Errno-based classification of network and resource-exhaustion failures on unix and windows.

Package trace provides enhanced error handling with stack traces, structured logging support, and full compatibility with Go 1.20+ error handling. @index Core error wrapping, stack capture, and structured error inspection APIs.

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func AccessDenied

func AccessDenied(msgAndArgs ...any) error

@intent classify authorization failures so callers can deny access consistently. @domainRule access denied errors are classified as HTTP 403 by the tracehttp package. @ensures records the current call site as the first trace frame. AccessDenied creates a new AccessDeniedError

func Aggregate

func Aggregate(errs ...error) error

@intent preserve multiple concurrent failures as one error value for later inspection. @domainRule nil errors are discarded so only real failures participate in aggregation. @ensures returns nil for an all-nil input set and the sole error unchanged for a single failure. Aggregate combines multiple errors into a single error using errors.Join (Go 1.20+)

func AlreadyExists

func AlreadyExists(msgAndArgs ...any) error

@intent classify duplicate-resource failures so callers can branch on uniqueness semantics. @domainRule already exists errors are classified as HTTP 409 by the tracehttp package. @ensures records the current call site as the first trace frame. AlreadyExists creates a new AlreadyExistsError

func As

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

@intent perform typed error extraction without repeating target boilerplate at call sites. @ensures returns the zero value of T and false when no matching error is found. As is a generic version of errors.As

func BadParameter

func BadParameter(msgAndArgs ...any) error

@intent classify invalid input so callers can branch on client-side request errors. @domainRule bad parameter errors are classified as HTTP 400 by the tracehttp package. @ensures records the current call site as the first trace frame. BadParameter creates a new BadParameterError

func Canceled

func Canceled(err error, msgAndArgs ...any) error

@intent wrap an existing cancellation cause as a typed trace error. @domainRule returns nil unchanged when the source error is nil. @ensures records the current call site as the first trace frame. Canceled wraps err as a CanceledError.

func CheckContext

func CheckContext(ctx context.Context) error

@intent provide a cheap guard for aborting work when the context is already done. @ensures returns nil while the context is still usable and a traced context error otherwise. CheckContext checks if context is still valid and returns error if not

func Conflict

func Conflict(msgAndArgs ...any) error

@intent classify state mismatches that prevent the requested operation from succeeding. @domainRule conflict errors are classified as HTTP 409 by the tracehttp package. @ensures records the current call site as the first trace frame. Conflict creates a new ConflictError

func ConnectionProblem

func ConnectionProblem(err error, msgAndArgs ...any) error

@intent mark infrastructure or network failures as transient connection problems. @domainRule connection problems are retryable and map to HTTP 503. @ensures prepends the current call site to any existing trace frames on the returned error. @ensures returns nil unchanged when the source error is nil. ConnectionProblem creates a new ConnectionProblemError. If err is nil, returns nil.

func ContextWithField

func ContextWithField(ctx context.Context, key string, value any) context.Context

@intent add one request-scoped field so later trace wrapping can include it. @domainRule the new value overrides an existing field with the same key. @mutates returns a derived context with an updated trace fields map. ContextWithField adds a single field to the context

func ContextWithFields

func ContextWithFields(ctx context.Context, fields map[string]any) context.Context

@intent accumulate request-scoped metadata that should be copied into later trace errors. @domainRule new field values override existing keys from the parent context. @mutates returns a derived context with a merged trace fields map. ContextWithFields adds fields to the context for error enrichment

func ContextWithTraceID

func ContextWithTraceID(ctx context.Context, traceID string) context.Context

@intent attach a request-scoped trace identifier so downstream errors and logs can be correlated. @mutates returns a derived context carrying the trace_id value. ContextWithTraceID adds a trace ID to the context

func ConvertSystemError

func ConvertSystemError(err error) error

@intent give callers one entry point that turns stdlib failures into trace's typed categories. @domainRule the first matching category wins, checked from most specific to least. @domainRule an error that already carries a trace category is returned unchanged so existing classification is never downgraded. @domainRule the converted error carries no trace message, so the operating system string stays in the cause for debugging instead of becoming a client-facing HTTP message. @ensures returns nil for nil input and the original error when no category applies. ConvertSystemError converts an operating system, filesystem, or network error into the matching typed trace error.

The original error stays reachable through errors.Is and errors.As. Errors that no rule matches are returned unchanged rather than forced into a category, so the caller can still wrap them itself.

func DebugReport

func DebugReport(err error) string

@intent render a full developer-facing report for nested and aggregated error chains. @domainRule aggregate errors must include every branch in the rendered report. @ensures returns an empty string when no error is provided. DebugReport returns a detailed report of the error chain

func DetachedContext

func DetachedContext(ctx context.Context) context.Context

@intent keep request metadata available for background work that must outlive request cancellation. @domainRule inherited values are preserved while cancellation is detached from the parent. DetachedContext returns a context that carries the parent's values but is not canceled when the parent is canceled (Go 1.21+). Useful for background cleanup or logging that should outlive the request.

func DoValue

func DoValue[T any](c *Contextualizer, fn func() (T, error)) (T, error)

@intent run a value-returning function and preserve its result while enriching any error with trace metadata. @ensures returns the function's value unchanged alongside a wrapped error when the call fails. DoValue executes a function returning a value and wraps any error

func Errorf

func Errorf(format string, args ...any) error

@intent create a traceable error from formatted application context. @ensures records the current call site as the first trace frame. @ensures allocates structured fields storage lazily on first field attachment. Errorf creates a new error with formatted message and stack trace

func Errors

func Errors(err error) iter.Seq[error]

@intent iterate every error reachable from wrapped and aggregated trace errors. @domainRule aggregate branches are traversed recursively, not flattened into a single message. @ensures yields each reachable error once per traversal path until the consumer stops. Errors returns an iterator over the error chain (Go 1.23+). It yields each error in the chain by following Unwrap() error and recursively traversing Unwrap() []error (e.g., AggregateError).

func FieldsFromContext

func FieldsFromContext(ctx context.Context) map[string]any

@intent expose request-scoped trace metadata without leaking mutable context state. @ensures returns a defensive copy of stored fields when trace metadata exists. FieldsFromContext retrieves fields from context. Returns a copy of the fields to prevent external mutation of the context value.

func FromContext

func FromContext(ctx context.Context) error

@intent translate context cancellation and deadline signals into trace-aware error values. @domainRule context.Cause is preferred so the original cancellation reason is preserved. @ensures captures the current call site and copies trace metadata from the context into the returned error. @ensures returns nil when the context has not been canceled. FromContext checks for context errors and wraps them appropriately. Uses context.Cause (Go 1.20+) to capture the cancellation cause when available, preserving the original reason for cancellation rather than just context.Canceled.

func GetFields

func GetFields(err error) map[string]any

@intent expose structured trace metadata for logging, transport, or inspection layers. @ensures returns a defensive copy of the stored fields when trace data exists. GetFields extracts fields from an error if available. Returns a copy of the fields to prevent external mutation.

func IsAccessDenied

func IsAccessDenied(err error) bool

@intent detect authorization failures anywhere in an error chain. @ensures returns false for nil errors. IsAccessDenied checks if error is an access denied error

func IsAlreadyExists

func IsAlreadyExists(err error) bool

@intent detect duplicate-resource failures anywhere in an error chain. @ensures returns false for nil errors. IsAlreadyExists checks if error is an already exists error

func IsBadParameter

func IsBadParameter(err error) bool

@intent detect caller-input failures anywhere in an error chain. @ensures returns false for nil errors. IsBadParameter checks if error is a bad parameter error

func IsCanceled

func IsCanceled(err error) bool

@intent detect cancellation semantics anywhere in an error chain. @ensures returns false for nil errors. IsCanceled checks if an error is a cancellation error

func IsConflict

func IsConflict(err error) bool

@intent detect state-conflict failures anywhere in an error chain. @ensures returns false for nil errors. IsConflict checks if error is a conflict error

func IsConnectionProblem

func IsConnectionProblem(err error) bool

@intent detect transient transport or infrastructure failures anywhere in an error chain. @ensures returns false for nil errors. IsConnectionProblem checks if error is a connection problem

func IsDeadlineExceeded

func IsDeadlineExceeded(err error) bool

@intent detect deadline-expired failures across both context and trace timeout wrappers. @ensures returns false for nil errors. IsDeadlineExceeded checks if an error is due to deadline exceeded

func IsLimitExceeded

func IsLimitExceeded(err error) bool

@intent detect throttling or quota failures anywhere in an error chain. @ensures returns false for nil errors. IsLimitExceeded checks if error is a limit exceeded error

func IsNotFound

func IsNotFound(err error) bool

@intent detect missing-resource failures anywhere in an error chain. @ensures returns false for nil errors. IsNotFound checks if error is a not found error

func IsNotImplemented

func IsNotImplemented(err error) bool

@intent detect unsupported-operation failures anywhere in an error chain. @ensures returns false for nil errors. IsNotImplemented checks if error is a not implemented error

func IsRetryable

func IsRetryable(err error) bool

@intent detect failures that explicitly advertise retry-safe semantics. @ensures returns false for nil errors. IsRetryable checks if error is retryable

func IsTimeout

func IsTimeout(err error) bool

@intent detect timeout failures anywhere in an error chain. @ensures returns false for nil errors. IsTimeout checks if error is a timeout error

func IsUnauthenticated

func IsUnauthenticated(err error) bool

@intent detect authentication failures anywhere in an error chain. @ensures returns false for nil errors. IsUnauthenticated checks if error is an unauthenticated error.

func LimitExceeded

func LimitExceeded(msgAndArgs ...any) error

@intent classify quota or rate-limit failures so callers can branch on throttling semantics. @domainRule limit exceeded errors are retryable and map to HTTP 429. @ensures records the current call site as the first trace frame. LimitExceeded creates a new LimitExceededError

func LogDebug

func LogDebug(ctx context.Context, logger *slog.Logger, msg string, err error, attrs ...slog.Attr)

@intent log an error at debug level using the trace serialization schema. @ensures does nothing when the input error is nil.

func LogError

func LogError(ctx context.Context, logger *slog.Logger, msg string, err error, attrs ...slog.Attr)

@intent log an error at error level using the trace serialization schema. @ensures does nothing when the input error is nil.

func LogWarn

func LogWarn(ctx context.Context, logger *slog.Logger, msg string, err error, attrs ...slog.Attr)

@intent log an error at warn level using the trace serialization schema. @ensures does nothing when the input error is nil.

func Must

func Must[T any](r Result[T]) T

@intent provide concise success-only access in contexts where failure should panic. @domainRule panics when the Result contains an error. Must unwraps a Result, panicking on error

func MustValue

func MustValue[T any](value T, err error) T

@intent collapse a Go-style value-plus-error pair when failure should panic immediately. @domainRule panics with a wrapped trace error when err is non-nil. MustValue unwraps a (value, error) pair, panicking on error

func New

func New(msg string) error

@intent create a fresh traceable application error at the current call site. @ensures records the current call site as the first trace frame. @ensures allocates structured fields storage lazily on first field attachment. New creates a new error with stack trace

func NotFound

func NotFound(msgAndArgs ...any) error

@intent classify a missing resource so callers can branch on lookup failure semantics. @domainRule not found errors are classified as HTTP 404 by the tracehttp package. @ensures records the current call site as the first trace frame. NotFound creates a new NotFoundError

Example
package main

import (
	"fmt"

	"github.com/tae2089/trace/v2"
)

func main() {
	err := trace.NotFound(fmt.Sprintf("user %s not found", "alice"))
	if trace.IsNotFound(err) {
		fmt.Println("User not found!")
	}
}
Output:
User not found!

func NotImplemented

func NotImplemented(msgAndArgs ...any) error

@intent classify unsupported behavior so callers can surface capability gaps consistently. @domainRule not implemented errors are classified as HTTP 501 by the tracehttp package. @ensures records the current call site as the first trace frame. NotImplemented creates a new NotImplementedError

func SlogError

func SlogError(err error) slog.Attr

@intent convert trace errors into a structured slog group that preserves message, cause, trace, and fields. @domainRule plain errors degrade to a minimal message-only structure instead of losing log compatibility. @ensures returns an empty slog.Attr when no error is provided. SlogError returns slog attributes for an error. Schema matches TraceError.LogValue: {"message":..., "cause":..., "trace":[...], "fields":{...}}

func SlogErrorValue

func SlogErrorValue(err error) slog.Value

@intent expose the trace-aware slog representation as a Value for callers building custom attributes. @ensures returns an empty string value when no error is provided. SlogErrorValue returns a slog.Value for an error

func Timeout

func Timeout(err error, msgAndArgs ...any) error

@intent classify operations that exceeded their allowed completion window. @domainRule timeout errors are retryable and map to HTTP 504. @ensures prepends the current call site to any existing trace frames on the returned error. @ensures returns nil unchanged when the source error is nil. Timeout creates a new TimeoutError. If err is nil, returns nil.

func TraceIDFromContext

func TraceIDFromContext(ctx context.Context) string

@intent retrieve the request-scoped trace identifier for correlation in logs and errors. @ensures returns an empty string when the context has no trace ID. TraceIDFromContext retrieves the trace ID from context

func Unauthenticated

func Unauthenticated(msgAndArgs ...any) error

@intent classify authentication failures separately from authorization failures. @domainRule unauthenticated errors map to HTTP 401 and expose only a fixed client message. @ensures records the current call site and keeps the supplied message for diagnostics only. Unauthenticated creates a new UnauthenticatedError.

func UserMessage

func UserMessage(err error) string

@intent extract the safest high-level message to show outside debugging channels. @domainRule prefer explicit TraceError messages before falling back to wrapped causes. @ensures returns an empty string when no error is provided. UserMessage returns a user-friendly error message without stack traces

func WithCancelCause

func WithCancelCause(parent context.Context) (context.Context, context.CancelCauseFunc)

@intent expose cancel-cause semantics through the trace package API so callers can preserve shutdown reasons. @ensures returned contexts can later surface their cause via context.Cause. WithCancelCause returns a context with a CancelCauseFunc (Go 1.20+). The cause can later be retrieved via context.Cause(ctx).

func WithField

func WithField(err error, key string, value any) error

@intent attach one diagnostic attribute without mutating the original error instance. @domainRule built-in trace wrapper types are preserved when replacing the inner TraceError. @mutates adds or replaces a single field on the returned error copy. @ensures returns nil unchanged when no source error is provided. WithField adds a field to the error for structured logging. It returns a new wrapper error with the field added, preserving the original error immutably.

func WithFields

func WithFields(err error, fields map[string]any) error

@intent attach multiple diagnostic attributes without mutating the original error instance. @domainRule later field values override earlier values for the same key. @mutates merges the provided fields into the returned error copy. @ensures returns nil unchanged when no source error is provided.

func WithTimeoutCause

func WithTimeoutCause(parent context.Context, d time.Duration, cause error) (context.Context, context.CancelFunc)

@intent create a timeout that preserves an explicit business cause for later error wrapping. @ensures the returned context is canceled after the deadline with the supplied cause. WithTimeoutCause returns a context that is canceled after the given duration with the specified cause error (Go 1.21+).

func Wrap

func Wrap(err error, msg ...string) error

@intent preserve the original error while adding call-site debugging context. @domainRule typed errors stay discoverable through the Err chain for errors.Is and errors.As. @ensures prepends the current call site to any existing trace frames on the returned error. @ensures returns nil unchanged when no source error is provided. Wrap wraps an error with stack trace information. If err is nil, Wrap returns nil. Wrap always creates a new TraceError, preserving the original error (including typed errors like NotFoundError) in the Err field.

Example

Example output

package main

import (
	"errors"
	"fmt"

	"github.com/tae2089/trace/v2"
)

func main() {
	err := errors.New("connection refused")
	wrapped := trace.Wrap(err, "failed to connect to database")
	fmt.Println(wrapped)
}

func WrapAccessDenied

func WrapAccessDenied(err error, msgAndArgs ...any) error

@intent preserve a lower-level cause while surfacing it as an authorization failure. @domainRule returns nil unchanged when the source error is nil. @ensures prepends the current call site to any existing trace frames on the returned error. WrapAccessDenied wraps an error as AccessDeniedError

func WrapAlreadyExists

func WrapAlreadyExists(err error, msgAndArgs ...any) error

@intent preserve an existing cause while reclassifying it as a duplicate-resource failure. @domainRule returns nil unchanged when the source error is nil. @ensures prepends the current call site to any existing trace frames on the returned error. WrapAlreadyExists wraps an error as AlreadyExistsError

func WrapBadParameter

func WrapBadParameter(err error, msgAndArgs ...any) error

@intent preserve an existing cause while surfacing it as a client input failure. @domainRule returns nil unchanged when the source error is nil. @ensures prepends the current call site to any existing trace frames on the returned error. WrapBadParameter wraps an error as BadParameterError

func WrapContext

func WrapContext(ctx context.Context, err error, msg ...string) error

@intent combine ordinary failures with request trace metadata before they cross a boundary. @domainRule trace_id and stored context fields are copied into the returned error when present. @ensures returns nil unchanged when no source error is provided. WrapContext wraps an error with context information

func WrapIfContextDone

func WrapIfContextDone(ctx context.Context, err error) error

@intent preserve both the operation failure and any concurrent context cancellation signal. @domainRule when the context is done, the returned error aggregates the original error with the context-derived failure. @ensures returns nil unchanged when no source error is provided. WrapIfContextDone wraps the error with context info if context is done

func WrapLimitExceeded

func WrapLimitExceeded(err error, msgAndArgs ...any) error

@intent reclassify an existing failure as a quota or rate-limit failure while keeping its cause. @domainRule returns nil unchanged when the source error is nil. @ensures prepends the current call site to any existing trace frames on the returned error. WrapLimitExceeded wraps an existing error as a LimitExceededError.

func WrapNotFound

func WrapNotFound(err error, msgAndArgs ...any) error

@intent preserve an existing cause while reclassifying it as a missing-resource failure. @domainRule returns nil unchanged when the source error is nil. @ensures prepends the current call site to any existing trace frames on the returned error. WrapNotFound wraps an error as NotFoundError

func WrapUnauthenticated

func WrapUnauthenticated(err error, msgAndArgs ...any) error

@intent preserve a lower-level cause while classifying an authentication failure. @domainRule returns nil unchanged when the source error is nil. WrapUnauthenticated wraps an error as UnauthenticatedError.

func WrapWithFields

func WrapWithFields(err error, fields map[string]any, msg ...string) error

@intent enrich an error with structured diagnostics that can flow into logs and HTTP responses. @domainRule field attachment must not discard the wrapped error chain. @mutates adds key-value metadata to the returned TraceError fields map. @ensures returns nil unchanged when no source error is provided. WrapWithFields wraps an error with stack trace and structured fields

func Wrapf

func Wrapf(err error, format string, args ...any) error

@intent preserve the original error while adding formatted debugging context. @domainRule typed errors stay discoverable through the Err chain for errors.Is and errors.As. @ensures returns nil unchanged when no source error is provided. Wrapf wraps an error with stack trace and a formatted message.

Types

type AccessDeniedError

type AccessDeniedError struct {
	*TraceError
}

@intent mark authorization failures so callers can deny access consistently. AccessDeniedError represents an access denied error

func (*AccessDeniedError) Error

func (e *AccessDeniedError) Error() string

@intent delegate user-facing string rendering to the embedded TraceError.

func (*AccessDeniedError) IsAccessDenied

func (e *AccessDeniedError) IsAccessDenied() bool

@intent advertise authorization-failure semantics for behavior-based error checks.

func (*AccessDeniedError) Unwrap

func (e *AccessDeniedError) Unwrap() error

@intent expose the embedded TraceError to standard Go error traversal.

type AggregateError

type AggregateError struct {
	Errs []error
}

@intent preserve multiple related failures as one traversable error tree. AggregateError holds multiple errors

func (*AggregateError) Error

func (e *AggregateError) Error() string

@intent render a readable summary that includes every child error in the aggregate. @ensures includes the number of collected errors in the output.

func (*AggregateError) Unwrap

func (e *AggregateError) Unwrap() []error

@intent expose all child errors so callers can traverse the aggregate tree. Unwrap returns the list of errors (Go 1.20+ multiple error unwrapping)

type AlreadyExistsError

type AlreadyExistsError struct {
	*TraceError
}

@intent mark failures caused by uniqueness or duplicate-resource conflicts. AlreadyExistsError represents an "already exists" error

func (*AlreadyExistsError) Error

func (e *AlreadyExistsError) Error() string

@intent delegate user-facing string rendering to the embedded TraceError.

func (*AlreadyExistsError) IsAlreadyExists

func (e *AlreadyExistsError) IsAlreadyExists() bool

@intent advertise duplicate-resource semantics for behavior-based error checks.

func (*AlreadyExistsError) Unwrap

func (e *AlreadyExistsError) Unwrap() error

@intent expose the embedded TraceError to standard Go error traversal.

type BadParameterError

type BadParameterError struct {
	*TraceError
}

@intent mark failures caused by invalid caller input or malformed parameters. BadParameterError represents an invalid parameter error

func (*BadParameterError) Error

func (e *BadParameterError) Error() string

@intent delegate user-facing string rendering to the embedded TraceError.

func (*BadParameterError) IsBadParameter

func (e *BadParameterError) IsBadParameter() bool

@intent advertise invalid-input semantics for behavior-based error checks.

func (*BadParameterError) Unwrap

func (e *BadParameterError) Unwrap() error

@intent expose the embedded TraceError to standard Go error traversal.

type CanceledError

type CanceledError struct {
	*TraceError
}

@intent represent context cancellation as a typed trace error that still carries the original cause. CanceledError represents a context cancellation error

func (*CanceledError) Error

func (e *CanceledError) Error() string

@intent delegate user-facing string rendering to the embedded TraceError.

func (*CanceledError) IsCanceled

func (e *CanceledError) IsCanceled() bool

@intent advertise cancellation semantics for behavior-based error checks.

func (*CanceledError) Unwrap

func (e *CanceledError) Unwrap() error

@intent expose the embedded TraceError to standard Go error traversal.

type ConflictError

type ConflictError struct {
	*TraceError
}

@intent mark state conflicts that block the requested operation until data changes. ConflictError represents a conflict error

func (*ConflictError) Error

func (e *ConflictError) Error() string

@intent delegate user-facing string rendering to the embedded TraceError.

func (*ConflictError) IsConflict

func (e *ConflictError) IsConflict() bool

@intent advertise state-conflict semantics for behavior-based error checks.

func (*ConflictError) Unwrap

func (e *ConflictError) Unwrap() error

@intent expose the embedded TraceError to standard Go error traversal.

type ConnectionProblemError

type ConnectionProblemError struct {
	*TraceError
}

@intent mark infrastructure or transport failures that may succeed on retry. ConnectionProblemError represents a connection error

func (*ConnectionProblemError) Error

func (e *ConnectionProblemError) Error() string

@intent delegate user-facing string rendering to the embedded TraceError.

func (*ConnectionProblemError) IsConnectionProblem

func (e *ConnectionProblemError) IsConnectionProblem() bool

@intent advertise transport-failure semantics for behavior-based error checks.

func (*ConnectionProblemError) IsRetryable

func (e *ConnectionProblemError) IsRetryable() bool

@intent advertise retry-safe semantics for transient connection failures.

func (*ConnectionProblemError) Unwrap

func (e *ConnectionProblemError) Unwrap() error

@intent expose the embedded TraceError to standard Go error traversal.

type Contextualizer

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

@intent bundle one context's trace metadata and cancellation hooks for reuse across multiple operations. Contextualizer wraps operations with context-aware error handling

func NewContextualizer

func NewContextualizer(ctx context.Context) *Contextualizer

@intent create a helper that consistently applies one context's trace metadata across multiple operations. @ensures returns a Contextualizer bound to the provided context. NewContextualizer creates a new Contextualizer

func (*Contextualizer) Do

func (c *Contextualizer) Do(fn func() error) error

@intent run a function and automatically enrich any resulting error with the contextualizer's trace metadata. @ensures returns nil when the function succeeds. Do executes a function and wraps any error with context

func (*Contextualizer) OnCancel

func (c *Contextualizer) OnCancel(fn func()) func() bool

@intent register cleanup or follow-up work that should run when the contextualizer's context is canceled. @sideEffect schedules a callback with the underlying context cancellation machinery. @ensures returns a stop function that can prevent the callback before cancellation. OnCancel registers fn to run after the context is canceled (Go 1.21+). Returns a stop function that prevents fn from running if called before cancellation.

func (*Contextualizer) Wrap

func (c *Contextualizer) Wrap(err error, msg ...string) error

@intent wrap an operation failure with the contextualizer's stored trace metadata. @ensures delegates to WrapContext using the contextualizer's context. Wrap wraps an error with context information

type ErrorAccessDenied

type ErrorAccessDenied interface {
	error
	IsAccessDenied() bool
}

@intent let callers recognize authorization failure semantics through behavior instead of concrete error types. ErrorAccessDenied indicates access was denied

type ErrorAlreadyExists

type ErrorAlreadyExists interface {
	error
	IsAlreadyExists() bool
}

@intent let callers recognize duplicate-resource semantics through behavior instead of concrete error types. ErrorAlreadyExists indicates a resource already exists

type ErrorBadParameter

type ErrorBadParameter interface {
	error
	IsBadParameter() bool
}

@intent let callers recognize invalid-input semantics through behavior instead of concrete error types. ErrorBadParameter indicates invalid input parameters

type ErrorCanceled

type ErrorCanceled interface {
	error
	IsCanceled() bool
}

@intent let callers recognize cancellation semantics through behavior rather than concrete types. ErrorCanceled is an interface for canceled errors

type ErrorConflict

type ErrorConflict interface {
	error
	IsConflict() bool
}

@intent let callers recognize state-conflict semantics through behavior instead of concrete error types. ErrorConflict indicates a conflict occurred

type ErrorConnectionProblem

type ErrorConnectionProblem interface {
	error
	IsConnectionProblem() bool
}

@intent let callers recognize transport-failure semantics through behavior instead of concrete error types. ErrorConnectionProblem indicates a connection issue

type ErrorHandler

type ErrorHandler struct {
	slog.Handler
}

@intent adapt slog handlers so error attributes are rewritten into the package's trace-aware schema. ErrorHandler wraps an slog.Handler to automatically extract trace information

func NewErrorHandler

func NewErrorHandler(h slog.Handler) *ErrorHandler

@intent wrap slog handlers so error attributes are consistently expanded into trace-aware structure. @domainRule nil handlers fall back to slog.DiscardHandler for safe optional logging. @ensures always returns a non-nil ErrorHandler. NewErrorHandler creates a new ErrorHandler wrapping the given handler

func (*ErrorHandler) Enabled

func (h *ErrorHandler) Enabled(ctx context.Context, level slog.Level) bool

@intent defer level checks to the wrapped handler so logging enablement stays consistent. @ensures returns the wrapped handler's enabled decision unchanged.

func (*ErrorHandler) Handle

func (h *ErrorHandler) Handle(ctx context.Context, r slog.Record) error

@intent rewrite incoming slog records so embedded error attributes use the trace logging schema. @sideEffect creates a replacement slog.Record and forwards it to the wrapped handler. @ensures non-error attributes are preserved verbatim.

func (*ErrorHandler) WithAttrs

func (h *ErrorHandler) WithAttrs(attrs []slog.Attr) slog.Handler

@intent preserve trace-aware error rewriting when callers derive a handler with additional attributes. @ensures returns another ErrorHandler wrapping the derived handler. WithAttrs returns a new handler with the given attributes

func (*ErrorHandler) WithGroup

func (h *ErrorHandler) WithGroup(name string) slog.Handler

@intent preserve trace-aware error rewriting when callers derive a grouped handler. @ensures returns another ErrorHandler wrapping the grouped handler. WithGroup returns a new handler with the given group name

type ErrorLimitExceeded

type ErrorLimitExceeded interface {
	error
	IsLimitExceeded() bool
}

@intent let callers recognize throttling or quota semantics through behavior instead of concrete error types. ErrorLimitExceeded indicates a rate limit or quota was exceeded

type ErrorNotFound

type ErrorNotFound interface {
	error
	IsNotFound() bool
}

@intent let callers recognize missing-resource semantics through behavior instead of concrete error types. ErrorNotFound indicates a resource was not found

type ErrorNotImplemented

type ErrorNotImplemented interface {
	error
	IsNotImplemented() bool
}

@intent let callers recognize unsupported-operation semantics through behavior instead of concrete error types. ErrorNotImplemented indicates functionality is not implemented

type ErrorRetryable

type ErrorRetryable interface {
	error
	IsRetryable() bool
}

@intent let callers recognize retry-safe failures through behavior instead of concrete error types. ErrorRetryable indicates an error that can be retried

type ErrorTimeout

type ErrorTimeout interface {
	error
	IsTimeout() bool
}

@intent let callers recognize timeout semantics through behavior instead of concrete error types. ErrorTimeout indicates an operation timed out

type ErrorUnauthenticated

type ErrorUnauthenticated interface {
	error
	IsUnauthenticated() bool
}

@intent let callers distinguish authentication failures from authorization failures. ErrorUnauthenticated indicates authentication is missing or invalid.

type Frame

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

@intent describe one recorded call site so errors and logs can point back to their origin. @domainRule only the program counter is stored; symbol resolution is deferred to render time because errors are created far more often than they are printed. Frame represents a single stack frame. It records the call site as a program counter and resolves the function name, file, and line lazily.

func CaptureFrame

func CaptureFrame(skip int) Frame

@intent capture the caller information that anchors trace output to a concrete source location. @intent let packages outside this module mint errors whose first frame points at their own caller. @ensures returns an empty frame when runtime caller information is unavailable. CaptureFrame captures a single stack frame at the given skip level.

skip follows runtime.Caller: 0 is CaptureFrame itself, 1 is its immediate caller, and 2 is the caller of that function. Constructors that want the frame to point at their own caller pass 2.

func (Frame) File

func (f Frame) File() string

@intent expose the resolved file base name for the recorded call site.

func (Frame) Function

func (f Frame) Function() string

@intent expose the resolved function name for the recorded call site.

func (Frame) Line

func (f Frame) Line() int

@intent expose the resolved line number for the recorded call site.

func (Frame) MarshalJSON

func (f Frame) MarshalJSON() ([]byte, error)

@intent keep the frame's JSON wire shape stable while the in-memory layout stays a bare program counter. @ensures emits the {"function":...,"file":...,"line":...} object shape used by earlier versions. MarshalJSON implements json.Marshaler.

func (Frame) String

func (f Frame) String() string

@intent render a single frame in a compact file-line-function format for debugging output. @ensures returns a string containing the file name, line number, and function name. String returns a human-readable representation of the frame

type Frames

type Frames []Frame

@intent represent an ordered stack trace that can be rendered or serialized with an error. Frames is a slice of stack frames

func GetFrames

func GetFrames(err error) Frames

@intent expose captured stack frames for diagnostics without leaking mutable internal state. @ensures returns a defensive copy of the stored frames when trace data exists. GetFrames extracts frames from an error if available. Returns a copy of the frames to prevent external mutation.

func (Frames) String

func (fs Frames) String() string

@intent render the recorded stack trace in call-order for compact error messages. @ensures returns an empty string when no frames are present. String returns a human-readable representation of all frames

type LimitExceededError

type LimitExceededError struct {
	*TraceError
}

@intent mark throttling or quota failures so callers can apply backoff behavior. LimitExceededError represents a rate limit or quota exceeded error

func (*LimitExceededError) Error

func (e *LimitExceededError) Error() string

@intent delegate user-facing string rendering to the embedded TraceError.

func (*LimitExceededError) IsLimitExceeded

func (e *LimitExceededError) IsLimitExceeded() bool

@intent advertise throttling semantics for behavior-based error checks.

func (*LimitExceededError) IsRetryable

func (e *LimitExceededError) IsRetryable() bool

@intent advertise retry-safe semantics for throttled operations.

func (*LimitExceededError) Unwrap

func (e *LimitExceededError) Unwrap() error

@intent expose the embedded TraceError to standard Go error traversal.

type NotFoundError

type NotFoundError struct {
	*TraceError
}

@intent mark failures caused by missing resources so callers can branch on lookup semantics. NotFoundError represents a "not found" error

func (*NotFoundError) Error

func (e *NotFoundError) Error() string

@intent delegate user-facing string rendering to the embedded TraceError.

func (*NotFoundError) IsNotFound

func (e *NotFoundError) IsNotFound() bool

@intent advertise missing-resource semantics for behavior-based error checks.

func (*NotFoundError) LogValue

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

@intent reuse TraceError structured logging output for not-found errors.

func (*NotFoundError) Unwrap

func (e *NotFoundError) Unwrap() error

@intent expose the embedded TraceError to standard Go error traversal.

type NotImplementedError

type NotImplementedError struct {
	*TraceError
}

@intent mark code paths that are recognized but intentionally unsupported. NotImplementedError represents a "not implemented" error

func (*NotImplementedError) Error

func (e *NotImplementedError) Error() string

@intent delegate user-facing string rendering to the embedded TraceError.

func (*NotImplementedError) IsNotImplemented

func (e *NotImplementedError) IsNotImplemented() bool

@intent advertise unsupported-operation semantics for behavior-based error checks.

func (*NotImplementedError) Unwrap

func (e *NotImplementedError) Unwrap() error

@intent expose the embedded TraceError to standard Go error traversal.

type Pipeline

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

@intent model stepwise transformations that should stop automatically after the first failure. Pipeline allows chaining operations that may fail

func NewPipeline

func NewPipeline[T any](value T) *Pipeline[T]

@intent start a chain of trace-aware transformations from an initial value. @ensures returns a pipeline with no initial error. NewPipeline creates a new pipeline with an initial value

func TransformPipeline

func TransformPipeline[T, U any](p *Pipeline[T], fn func(T) (U, error)) *Pipeline[U]

@intent continue a pipeline while changing the value type and preserving trace-aware failure handling. @ensures carries forward existing pipeline errors without invoking fn. TransformPipeline transforms between different types

func (*Pipeline[T]) Recover

func (p *Pipeline[T]) Recover(fn func(error) (T, error)) *Pipeline[T]

@intent give failed pipelines a chance to replace their error with a recovery value or new error. @ensures calls fn only when the pipeline is currently failed. Recover attempts to recover from an error

func (*Pipeline[T]) RecoverWith

func (p *Pipeline[T]) RecoverWith(defaultVal T) *Pipeline[T]

@intent replace a failed pipeline with a caller-provided default value. @ensures clears the stored error when recovery is applied. RecoverWith recovers with a default value

func (*Pipeline[T]) Result

func (p *Pipeline[T]) Result() (T, error)

@intent expose the pipeline state as a conventional Go value-plus-error pair. @ensures returns the current pipeline value and error unchanged. Result returns the final value and error

func (*Pipeline[T]) Then

func (p *Pipeline[T]) Then(fn func(T) (T, error)) *Pipeline[T]

@intent apply the next transformation only while the pipeline remains successful. @ensures wraps new step failures with trace context before storing them. Then executes the function if no error has occurred

func (*Pipeline[T]) ThenDo

func (p *Pipeline[T]) ThenDo(fn func(T) error) *Pipeline[T]

@intent run a side-effecting validation or action without changing the pipeline value. @ensures wraps new step failures with trace context before storing them. ThenDo executes a function that doesn't modify the value

func (*Pipeline[T]) ToResult

func (p *Pipeline[T]) ToResult() Result[T]

@intent convert pipeline state into the Result abstraction for further composition. @ensures returns a Result containing the current pipeline value and error. ToResult converts the pipeline to a Result

type Result

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

@intent model success and failure as one value so callers can compose operations without losing trace context. Result represents either a value or an error (similar to Rust's Result)

Example
package main

import (
	"fmt"
	"os"

	"github.com/tae2089/trace/v2"
)

func main() {
	result := trace.Try(os.Open("nonexistent.txt"))
	value := result.UnwrapOr(nil)
	if value == nil {
		fmt.Println("File not found, using default")
	}
}
Output:
File not found, using default

func Collect

func Collect[T any](results ...Result[T]) Result[[]T]

@intent combine many Result values into one collection while preserving all failures. @domainRule any failed input produces an aggregated error instead of a partial success. Collect collects multiple Results into a single Result containing a slice

func Err

func Err[T any](err error) Result[T]

@intent convert a failure into a Result while preserving trace metadata for downstream composition. @ensures wraps the provided error with trace context before storing it. Err creates a failed Result

func ErrMsg

func ErrMsg[T any](msg string) Result[T]

@intent create a failed Result directly from an application message when no underlying error exists. @ensures records the current call site as the first trace frame. ErrMsg creates a failed Result with a message

func FlatMap

func FlatMap[T, U any](r Result[T], fn func(T) Result[U]) Result[U]

@intent chain operations that already return Result values without manual error branching. @ensures skips fn and preserves the existing error when the Result is failed. FlatMap chains Result-returning operations

func Map

func Map[T, U any](r Result[T], fn func(T) U) Result[U]

@intent transform successful results while leaving failures untouched. @ensures preserves the existing error without calling fn when the Result is failed. Map transforms the value if present

func MapErr

func MapErr[T any](r Result[T], fn func(error) error) Result[T]

@intent rewrite failure values without changing successful payloads. @ensures leaves successful results unchanged. MapErr transforms the error if present

func Ok

func Ok[T any](value T) Result[T]

@intent wrap a successful value in the Result abstraction without adding trace overhead. @ensures returns a Result with no error. Ok creates a successful Result

func Try

func Try[T any](value T, err error) Result[T]

@intent lift ordinary Go return pairs into a Result for composable error handling. @ensures wraps non-nil errors with trace context before storing them. Try wraps a function call that returns (T, error) into a Result

func (Result[T]) Error

func (r Result[T]) Error() error

@intent expose the failure component directly for interoperability with Go error handling. @ensures returns nil for successful results. Error returns the error if present

func (Result[T]) IsErr

func (r Result[T]) IsErr() bool

@intent let callers branch on failure without unpacking the Result payload. @ensures returns true only when the Result holds an error. IsErr returns true if the Result contains an error

func (Result[T]) IsOk

func (r Result[T]) IsOk() bool

@intent let callers branch on success without unpacking the Result payload. @ensures returns true only when the Result holds no error. IsOk returns true if the Result contains a value

func (Result[T]) Unwrap

func (r Result[T]) Unwrap() T

@intent extract the successful value in contexts where failure should abort immediately. @domainRule panics with the stored error when the Result is failed. Unwrap returns the value or panics if there's an error

func (Result[T]) UnwrapOr

func (r Result[T]) UnwrapOr(defaultVal T) T

@intent provide a fallback value when the Result is failed. @ensures returns the stored value on success and the provided default on failure. UnwrapOr returns the value or the provided default

func (Result[T]) UnwrapOrElse

func (r Result[T]) UnwrapOrElse(fn func(error) T) T

@intent derive a fallback value from the failure reason when the Result is failed. @ensures calls the fallback function only when the Result holds an error. UnwrapOrElse returns the value or calls the function to get a default

func (Result[T]) Value

func (r Result[T]) Value() (T, error)

@intent bridge Result back into Go's conventional value-plus-error calling style. @ensures returns the stored value and stored error unchanged. Value returns the value and error separately (Go-style)

type TimeoutError

type TimeoutError struct {
	*TraceError
}

@intent mark operations that exceeded their allowed completion window. TimeoutError represents a timeout error

func (*TimeoutError) Error

func (e *TimeoutError) Error() string

@intent delegate user-facing string rendering to the embedded TraceError.

func (*TimeoutError) IsRetryable

func (e *TimeoutError) IsRetryable() bool

@intent advertise retry-safe semantics for timeout failures.

func (*TimeoutError) IsTimeout

func (e *TimeoutError) IsTimeout() bool

@intent advertise timeout semantics for behavior-based error checks.

func (*TimeoutError) Unwrap

func (e *TimeoutError) Unwrap() error

@intent expose the embedded TraceError to standard Go error traversal.

type TraceError

type TraceError struct {
	// Original error being wrapped
	Err error
	// Message is additional context
	Message string
	// Frames contains the stack trace
	Frames Frames
	// Fields contains structured data for logging
	Fields map[string]any
}

@intent serve as the canonical trace-aware error wrapper carrying cause, message, frames, and structured fields. TraceError is the core error type that captures stack traces

func (*TraceError) Error

func (e *TraceError) Error() string

@intent produce the primary human-readable message for traced errors and their wrapped causes. @ensures includes frame and cause information when available. Error implements the error interface

func (*TraceError) Format

func (e *TraceError) Format(s fmt.State, verb rune)

@intent support concise and verbose formatting styles for traced errors. @ensures %+v output includes stack frames and structured fields when present. Format implements fmt.Formatter for customizable output

func (*TraceError) LogValue

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

@intent serialize traced errors into structured slog fields without losing cause or stack context. @ensures includes message, cause, trace, and fields only when those values are present. LogValue implements slog.LogValuer for structured logging. Schema: {"message":..., "cause":..., "trace":[{"file":..., "line":..., "func":...}], "fields":{...}}

func (*TraceError) Unwrap

func (e *TraceError) Unwrap() error

@intent expose the wrapped cause so traced errors participate in standard Go error traversal. @ensures returns the original wrapped error. Unwrap implements the errors.Unwrap interface for Go 1.13+

type TraceErrorReplacer

type TraceErrorReplacer interface {
	ReplaceTraceError(original, replacement *TraceError) (rebuilt error, ok bool)
}

@intent let custom wrapper types survive field updates instead of being peeled away. @domainRule the wrapper must return ok=false when original is not its direct inner TraceError. TraceErrorReplacer lets wrapper types outside this package survive WithField and WithFields. When those functions rebuild an error chain they replace the inner *TraceError; wrappers this package does not know are otherwise dropped. A wrapper that implements this method is asked to rebuild itself around replacement and report ok=true, or ok=false if original is not its inner TraceError.

type UnauthenticatedError

type UnauthenticatedError struct {
	*TraceError
}

@intent mark authentication failures independently from authorization failures. UnauthenticatedError represents a missing or invalid authentication identity.

func (*UnauthenticatedError) Error

func (e *UnauthenticatedError) Error() string

@intent delegate developer-facing rendering to the embedded TraceError.

func (*UnauthenticatedError) IsUnauthenticated

func (e *UnauthenticatedError) IsUnauthenticated() bool

@intent advertise authentication-failure semantics for behavior-based checks.

func (*UnauthenticatedError) Unwrap

func (e *UnauthenticatedError) Unwrap() error

@intent expose the embedded TraceError to standard Go error traversal.

Directories

Path Synopsis
@index Client-side example for safely restoring typed trace errors from public HTTP error responses.
@index Client-side example for safely restoring typed trace errors from public HTTP error responses.
@index HTTP adapters that translate trace errors into API responses, middleware behavior, and client-side classifications.
@index HTTP adapters that translate trace errors into API responses, middleware behavior, and client-side classifications.

Jump to

Keyboard shortcuts

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