Simple custom error wrapper utility library for my Go projects.
Find a file
2026-04-25 18:50:14 +00:00
.gitignore Initial commit 2026-04-25 18:33:33 +00:00
errx.go add errx 2026-04-25 18:50:14 +00:00
errx_test.go add errx 2026-04-25 18:50:14 +00:00
example_test.go add errx 2026-04-25 18:50:14 +00:00
go.mod add errx 2026-04-25 18:50:14 +00:00
README.md add errx 2026-04-25 18:50:14 +00:00
Taskfile.yml add errx 2026-04-25 18:50:14 +00:00

errx

Simple custom error wrapper utility library for my Go projects.

errx records the operation where each error happened and chains those operations together as the error bubbles up. The result is a readable breadcrumb instead of a runtime stack trace:

users.Get: user=42: db.Query: connection refused

Usage

Each function declares its op and wraps errors as it returns them:

import "git.juancwu.dev/juancwu/errx"

func (s *Store) Get(id int) (*User, error) {
    const op = "users.Get"

    row, err := s.db.Query(id)
    if err != nil {
        return nil, errx.Wrapf(op, err, "user=%d", id)
    }
    if row == nil {
        return nil, errx.New(op, "not found")
    }
    return row, nil
}

The four constructors:

errx.New(op, msg)                        // fresh error, static msg
errx.Newf(op, format, args...)           // fresh error, formatted msg
errx.Wrap(op, err)                       // wrap, no extra msg (nil-safe)
errx.Wrapf(op, err, format, args...)     // wrap with formatted msg (nil-safe)

Wrap and Wrapf return nil when passed a nil error, so you can chain them without an extra guard:

return errx.Wrap(op, s.commit())

Interop

*errx.Error implements Unwrap, so errors.Is and errors.As walk the chain as expected:

if errors.Is(err, io.EOF) { ... }

var e *errx.Error
if errors.As(err, &e) {
    log.Printf("op=%s", e.Op)
}