beterr

package module
v0.6.0 Latest Latest
Warning

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

Go to latest
Published: May 17, 2026 License: MIT Imports: 5 Imported by: 0

README

beterr

Go Reference Go Report Card

A lightweight Go package for structured error handling and wrapping with enhanced error formatting, function call context, and argument inspection.

Features

  • Structured Error Formatting: Wrap errors with function context and arguments for better debugging
  • Automatic Call Stack Information: Capture function names using runtime reflection
  • Argument Inspection: Include function arguments in error output
  • JSON Serialization: Convert complex data structures to readable JSON format
  • Error Chaining: Support for nested error structures with full context preservation
  • Zero Dependencies: Uses only Go standard library
  • Lightweight: Minimal performance overhead

Installation

go get github.com/StevenM2002/beterr

Quick Start

package main

import (
    "fmt"
    "github.com/StevenM2002/beterr"
)

func processUser(userID int, name string) error {
    w := beterr.W(userID, name)
    
    if userID < 0 {
        return w.E(fmt.Errorf("invalid user ID"), "failed to process user")
    }
    
    return nil
}

func main() {
    err := processUser(-1, "John")
    if err != nil {
        fmt.Println(err)
        // Output: {"fn_name":"main.processUser","args":[-1,"John"],"msg":"failed to process user","inner":"invalid user ID"}
    }
}

Usage Examples

Basic Error Wrapping
func validateInput(data string) error {
    w := beterr.W(data)
    
    if len(data) == 0 {
        return w.E(fmt.Errorf("empty input"), "validation failed")
    }
    
    return nil
}
With Context and Complex Types
import "context"

type User struct {
    ID   int    `json:"id"`
    Name string `json:"name"`
}

func handleRequest(ctx context.Context, user *User) error {
    w := beterr.W(ctx, user)
    
    err := validateUser(user)
    if err != nil {
        return w.E(err, "request processing failed")
    }
    
    return nil
}
Struct Serialization Utility
type Config struct {
    Host string `json:"host"`
    Port int    `json:"port"`
}

config := Config{Host: "localhost", Port: 8080}
jsonStr := beterr.StructString(config)
fmt.Println(jsonStr) // {"host":"localhost","port":8080}

API Reference

Types
Wrap
type Wrap struct {
    A []any // Arguments to include in wrap output
}

The Wrap struct is the main type for creating structured error contexts. The A field holds arguments that will be serialized and included in the error output.

Methods
E(err error, msg ...string) error

Formats an error with wrapping context including:

  • Function name (automatically captured)
  • Arguments from the A field
  • Custom message
  • Original error (supports chaining)

Parameters:

  • err: The original error to wrap
  • msg: Optional message parts that will be joined with spaces

Returns: A new error with structured wrapping information

Functions
W(args ...any) *Wrap

A convenience function that creates a new Wrap instance with the provided arguments. This is equivalent to &Wrap{A: []any{...}} but more concise.

Parameters:

  • args: Variadic arguments to include in the wrap context

Returns: A pointer to a new Wrap instance

Example:

w := beterr.W(userID, requestData, config)
return w.E(err, "failed to process request")
StructString(v any) string

Converts any value to a JSON string representation. If JSON marshaling fails, it falls back to Go's default string formatting.

Parameters:

  • v: Any value to serialize

Returns: JSON string representation or fallback string format

Error Output Format

The package produces structured JSON error output with the following fields:

{
  "fn_name": "main.processUser",
  "args": [-1, "John"],
  "msg": "failed to process user",
  "inner": "invalid user ID"
}
  • fn_name: Fully qualified function name where the error occurred
  • args: Arguments passed to the Wrap struct, preserved at their native JSON types
  • msg: Custom error message
  • inner: The original error string, or a nested error object for chained wraps

Error Chaining

The package supports full error chaining, preserving the complete context chain:

func level1() error {
    w := beterr.W("level1-arg")
    return w.E(level2(), "level1 failed")
}

func level2() error {
    w := beterr.W("level2-arg")
    return w.E(fmt.Errorf("original error"), "level2 failed")
}

This creates a nested structure showing the complete error path with context at each level.

License

This project is licensed under the MIT License - see the LICENSE file for details.

Acknowledgments

  • Inspired by the need for better error wrapping in Go applications
  • Built with Go's excellent standard library for runtime introspection

Documentation

Overview

Package beterr provides structured error handling and debugging utilities for Go applications. It offers enhanced error formatting with function call context and argument inspection.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func StructString

func StructString(v any) string

StructString converts any value to a JSON string representation. If JSON marshaling fails, it falls back to the default string format.

Types

type Error added in v0.4.0

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

Error is the error returned by Wrap.E. It carries the original underlying error alongside a JSON-serialised description of the wrap (function name, arguments, message, and any nested chain), plus a pre-built shallow view of the immediate previous error for Top to return.

func (*Error) Error added in v0.4.0

func (e *Error) Error() string

Error returns the JSON-serialised wrap including any nested chain.

func (*Error) Top added in v0.4.0

func (e *Error) Top() error

Top returns the immediate previous error this wrap was applied to, with any further beterr chain stripped. If the wrapped error was itself a beterr Error, Top returns a shallow rendition of it — that layer's own fn_name, args, and msg, with its inner set to null. If the wrapped error was a plain error, Top returns it as-is. If the wrap was created over a nil error, Top returns nil.

Use Top when forwarding an error to a caller who shouldn't see the full nested error stack — for example, returning errors from an RPC handler:

wrapped := beterr.W(userID).E(err, "failed to process request")
log.Println(wrapped)                            // full nested debug JSON
return connect.NewError(code, wrapped.Top())    // only the last error

func (*Error) Unwrap added in v0.5.0

func (e *Error) Unwrap() error

Unwrap returns the underlying error so errors.Is and errors.As traverse beterr wraps the same way they traverse fmt.Errorf("%w", ...) wraps. Sentinel checks against the original error continue to work after any number of beterr layers have been added.

type Wrap added in v0.1.2

type Wrap struct {
	// A holds arguments to be included in debug output
	A []any
}

Wrap provides debugging functionality with argument tracking. The A field stores arguments that will be included in error output.

func W added in v0.1.4

func W(args ...any) *Wrap

W creates a new Wrap instance with the provided arguments. This is a convenience function that internally calls Wrap{A: []any{...}}. It accepts any number of arguments which will be included in error output for debugging.

Example usage:

w := W(userID, requestData, config)
return w.E(err, "failed to process request")

func (*Wrap) E added in v0.1.2

func (w *Wrap) E(err error, msg ...string) *Error

E formats an error with debugging context including function name, arguments, and message. It wraps the original error with structured debugging information that can be chained. The returned *Error implements the error interface, exposes Unwrap so errors.Is/As traverse the chain, and exposes Top to release only the immediate previous error without leaking the deeper stack.

Jump to

Keyboard shortcuts

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