Help us shape the future of Go! Take the 2026 Go Developer Survey

erax

package module
v0.4.1 Latest Latest
Warning

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

Go to latest
Published: Aug 2, 2026 License: MIT Imports: 7 Imported by: 0

README

🍃 v0.4.0 Is Here

This release is a massive step forward.

The core pipeline is now up to 5x faster, uses up to 76% less memory, and cuts allocations by up to 98%. Check out the benchmarks!

We also redesigned the API to make common workflows simpler, added support for nested error trees, and cleaned up a lot of rough edges.

Enjoy!

About

erax is a Go package that treats errors as first-class data structures rather than plain strings.

It provides error chaining, structured metadata, efficient serialization, and beautiful CLI error traces, allowing a single error to serve as the source of truth for developers, machines, and observability systems.

Instead of duplicating context across logging, APIs, and tracing, erax keeps semantic information attached to the error itself, making it easy to render, serialize, inspect, and transport without losing structure.

Example Usage

func CreateUser() error {
    err := erax.New("email is already in use")

    err = erax.WithMeta(
        err,
        "failed to create user",
        erax.F("code", "503"),
        erax.F("info", "This is a really\nreally long information."),
        erax.F("user_error", "An account with this email already exists."),
    )
    return err
}

func Register() error {
    err := CreateUser()
    return erax.WrapWithErrors(
        err,
        "failed to register\nbecause of ducks!",
        erax.New("random error"),
    )
}

func main() {
    err := Register()
    if err != nil {
        erax.Format(err)
    }
}

Result:

image

Result (JSON):

{
  "cause": [
    {
      "message": "random error"
    },
    {
      "cause": {
        "message": "email is already in use"
      },
      "message": "failed to create user",
      "meta": {
        "code": "503",
        "info": "This is a really\nreally long information.",
        "user_error": "An account with this email already exists."
      }
    }
  ],
  "message": "failed to register\nbecause of ducks!"
}

[!Tip] You might want to check out the full example

🖤 Features

  • 🌈 Styled and readable error trace output for CLI
  • 🔗 Error chaining
  • 🏷️ Attach and retrieve key-value metadata
  • 🎨 Configurable colors for trace output
  • 🔄 Compatible with standard and third-party errors (e.g., pkg/errors)
  • ✨ Fast JSON serialization / deserialization

🔥 Wanna start NOW?

Below is a lame section for those who may feel unconfident.

If you are already cool enough - check out the How To Use guide.

👁 Why?

Most Go applications treat errors as strings.

As an application grows, every layer starts solving the same problem independently:

  • logging decides how to serialize an error;
  • APIs decide what information should be exposed to clients;
  • tracing systems collect additional context;
  • developers reconstruct the original failure from logs and stack traces.

The result is that the same information is duplicated across multiple systems, often in different formats.

[!Important] erax takes a different approach.

An error becomes the single source of truth that carries its own structure, metadata, and context. The same error can be rendered for a developer, serialized for logging, or inspected by machines without duplicating information or losing semantics.

For developers

Reading long error chains in logs is frustrating.

Traditional wrapped errors eventually degrade into long text messages where the original failure becomes difficult to identify.

Once a chain grows large enough, developers usually stop reading it and jump directly to logs or stack traces.

Stack traces help locate where something happened, but they rarely explain why.

[!Important] erax keeps the structure of the error chain intact and provides a readable error trace instead of flattening everything into text.

You can:

  • print an error directly using erax.Format();
  • deserialize the same error from logs and render exactly the same structured trace.

The result is a representation that is easier to understand than raw logs and significantly more focused than a stack trace.

For logging

Logging libraries should not define the structure of your errors.

In many applications, every logging package serializes errors differently. Some flatten them into strings, some inspect them via reflection, while others require custom adapters.

[!Important] With erax, serialization belongs to the error itself.

Errors can be converted into a compact, deterministic JSON representation that is fast and reflection-free.

Instead of teaching every logging backend how to understand your errors, the error already knows how to represent itself.

Metadata belongs to the error

Applications constantly need additional information about failures:

  • status codes;
  • error codes;
  • retry hints;
  • user-facing messages;
  • arbitrary diagnostic context.

Without a common representation, every layer invents its own way to transport this information.

[!Important] erax keeps metadata attached to the error itself, allowing it to travel through the application together with the failure.

Why not custom error types?

Custom error types are perfectly reasonable. The problem appears when there are hundreds of them.

Large codebases often end up with:

  • dozens of packages exporting error types;
  • extensive errors.As() chains;
  • tight coupling between unrelated packages;
  • APIs that depend on implementation details.

Custom error types are usually written as part of a feature, not as reusable infrastructure. Their primary goal is to solve today's problem, not to become a stable, high-performance error model for an entire organization.

As a result, every team gradually invents its own conventions for metadata, serialization, wrapping, and interoperability. Over time, these implementations diverge, are rewritten by new developers, and become increasingly difficult to use consistently across services.

[!Important] erax keeps the error model consistent while allowing structured metadata without requiring every failure to become a new Go type.

Why not tracing?

Using tracing as the primary carrier of error context comes at a significant cost. Every additional attribute, event, or span increases telemetry volume, memory usage, network traffic, and storage costs. In high-throughput systems, that overhead quickly becomes noticeable.

More importantly, the semantic meaning of an error becomes scattered across tracing infrastructure instead of traveling with the error itself. Outside the tracing system, much of that context is simply unavailable.

[!Important] erax keeps error semantics inside the error, where they belong.

Tracing remains responsible for request execution and performance analysis, while errors remain responsible for describing failures.

The two complement each other, but neither should replace the other.

⚡ Why is it Fast?

erax is fast because it aggressively avoids unnecessary allocations, has no hidden overhead, and keeps all hot paths simple:

  • Most functions use early returns, so in the common case nothing is allocated or wrapped.
  • It relies on pre-sized slices, manual loops, and strings.Builder with grow hints, instead of reflection or standard library heavy helpers.
  • Traversal is done with a small fixed stack instead of recursion, which avoids heap pressure and keeps lookups cheap.
  • JSON serialization is optimized with a sync.Pool buffer reuse, so repeated formatting doesn't constantly allocate new memory.

[!Tip] Performance comparison summary:

  • ▶ erax.Wrap(err, "msg") vs fmt.Errorf("msg: %w", err) vs pkgerrors.Wrap(err, "msg")
  • ▶ erax.WithMeta vs otel/trace span.SetAttributes
  • ▶ JSON serialisation - erax vs zap

🔮 Future features (coming soon)

In a short while, you will witness the following things:

  • native []byte JSON API
  • GetMetas() []MetaField
  • no-color mode
  • ASCII branch style
  • square branch style
  • maybe some performance improvements

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func AddMeta

func AddMeta(err error, message string, key, value string) error

AddMeta is useful when metadata is added incrementally.

If the error is already an erax error, only the key-value pair is added. The message argument is ignored in that case, so it should be left empty.

If you already know all fields, prefer WithMeta instead, as it performs fewer allocations.

func Cast

func Cast(err error) error

func Format

func Format(err error) string

Format pretty-prints the error trace.

You're a good person, so please don't feed it non-erax errors, okay?

It won't break anything (it's just %+v), but... why would you even do that?

func FormatToJSONMap

func FormatToJSONMap(err error) map[string]any

FormatToJSONMap converts an error to a JSON-compatible map representation. Handles both erax and standard Go errors.

func FormatToJSONString

func FormatToJSONString(err error) string

FormatToJSONString converts an error to a JSON string representation.

Uses a sync.Pool for buffer reuse to improve performance.

func FromJSONMap

func FromJSONMap(m map[string]any) error

FromJSONMap reconstructs an erax error from a JSON map representation.

Use this to deserialize errors previously serialized with FormatToJSONMap.

func GetMeta

func GetMeta(err error, key string) (string, bool)

GetMeta searches for a metadata field by key across the entire error chain.

It searches from the most recent error backwards through causes and children.

func IsErax

func IsErax(err error) bool

IsErax checks whether an error is an erax error type.

func New

func New(message string) error

New creates a new standard Go error with the given message.

func SetAlienColor

func SetAlienColor(color lipgloss.Color)

SetAlienColor sets the color for non-erax errors in formatted output.

func SetBranchColor

func SetBranchColor(color lipgloss.Color)

SetBranchColor sets the color for tree branch characters in formatted output.

func SetErrorColor

func SetErrorColor(color lipgloss.Color)

SetErrorColor sets the color for erax error messages in formatted output.

func SetKeyColor

func SetKeyColor(color lipgloss.Color)

SetKeyColor sets the color for metadata keys in formatted output.

func SetValueColor

func SetValueColor(color lipgloss.Color)

SetValueColor sets the color for metadata values in formatted output.

func WithMeta

func WithMeta(err error, message string, fields ...MetaField) error

WithMeta wraps an error with metadata and a new message.

If the error is nil, returns nil. If no fields are provided, returns the original error unchanged.

func Wrap

func Wrap(err error, message string) error

Wrap wraps an error with a new message.

If the error is nil, returns nil.

func WrapCast

func WrapCast(err error, message string) error

WrapCast wraps an error with a new message.

If the error is nil, returns nil.

Implicitly casts errors to erax type.

func WrapWithErrors

func WrapWithErrors(err error, message string, newErrors ...error) error

WrapWithErrors wraps an error with multiple additional errors.

func WrapWithErrorsCast

func WrapWithErrorsCast(err error, message string, newErrors ...error) error

WrapWithErrorsCast wraps an error with multiple additional errors.

Implicitly casts errors to erax type.

Types

type MetaField

type MetaField struct {
	Key, Value string
}

MetaField represents a key-value pair for error metadata.

func F

func F(k, v string) MetaField

F is a convenience function to create a MetaField.

Directories

Path Synopsis
erax module

Jump to

Keyboard shortcuts

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