Overview
errorc — structured context for Go errors
errorc is a small Go library that augments the standard error type with structured context while keeping compatibility with errors.Is and errors.As.
It helps you create errors like:
invalid input, field1: value1, field2: value2query failed, retries: 3, cached: falseoperation failed, cause: disk fullstorage: read_failed
The API is intentionally small:
errorc.New(...)creates a new error.errorc.With(...)wraps an existing error with context fields.errorc.String(...),errorc.Int(...),errorc.Bool(...), anderrorc.Error(...)create fields.errorc.WithNamespace(...),errorc.Namespace, anderrorc.ErrorFactory(...)help build namespaced error identifiers.
For structured field keys such as auth.user.id or http.request.id, use the companion keys library.
- Repository: github.com/ygrebnov/errorc
- API Docs: pkg.go.dev/github.com/ygrebnov/errorc
Quick start
package main
import (
"errors"
"fmt"
"github.com/ygrebnov/errorc"
"github.com/ygrebnov/keys"
)
func main() {
ErrInvalidInput := errorc.New("invalid input")
userKey := keys.Factory(keys.WithSegments("user"))
err := errorc.With(
ErrInvalidInput,
errorc.String(userKey("id"), "123"),
errorc.Bool("cached", false),
)
if errors.Is(err, ErrInvalidInput) {
fmt.Println(err)
}
// Output:
// invalid input, user.id: 123, cached: false
}
Why use it?
- Preserves error identity for
errors.Isanderrors.As. - Adds structured fields without giving up plain
errorinteroperability. - Supports primitive field helpers for strings, ints, bools, and nested errors.
- Pairs naturally with
keysfor hierarchical field names.
Core concepts
With
Use errorc.With(err, fields...) to attach structured context to an existing error.
err := errorc.With(
errorc.New("query failed"),
errorc.String("query", "select * from users"),
errorc.Int("retries", 3),
)
fmt.Println(err) // query failed, query: select * from users, retries: 3
String, Int, Bool, and Error
Use the field helpers for common value types.
cause := errors.New("disk full")
err := errorc.With(
errorc.New("operation failed"),
errorc.Bool("retryable", false),
errorc.Error("cause", cause),
)
fmt.Println(err) // operation failed, retryable: false, cause: disk full
Sentinel errors
Because errorc.With(...) wraps the original error, you can still use errors.Is.
ErrInvalidInput := errorc.New("invalid input")
err := errorc.With(ErrInvalidInput, errorc.String("field", "email"))
fmt.Println(errors.Is(err, ErrInvalidInput)) // true
Typed errors
Typed errors remain discoverable through errors.As.
type ValidationError struct{ Message string }
func (e *ValidationError) Error() string { return e.Message }
err := errorc.With(&ValidationError{"invalid input"}, errorc.String("field", "email"))
var ve *ValidationError
fmt.Println(errors.As(err, &ve)) // true
Namespaced errors
Use namespaces when you want simple identifiers like storage: read_failed.
storageErr := errorc.ErrorFactory("storage")
fmt.Println(storageErr("read_failed")) // storage: read_failed
For dotted structured keys such as storage.read.failed, prefer the keys library.
Structured keys with keys
errorc field helpers are generic over any type whose underlying type is string, which means they accept keys.Key values directly.
userIDKey := keys.New("id", keys.WithSegments("user"))
traceKey := keys.New("trace_id", keys.WithSegments("request"))
err := errorc.With(
errorc.New("invalid input"),
errorc.String(userIDKey, "123"),
errorc.String(traceKey, "abc-xyz"),
)
fmt.Println(err)
// invalid input, user.id: 123, request.trace_id: abc-xyz
If your project uses hierarchical key names consistently across logs, metrics, and errors, see the keys overview and keys examples.
Compared to fmt.Errorf
The errorc.With function behaves similarly to fmt.Errorf for adding context, but performs significantly faster in the project benchmarks:
BenchmarkWith-8 53965288 21.81 ns/op
BenchmarkFmtErrorf-8 7401583 186.7 ns/op
Notes
errorc.With(nil, ...)returnsnil.- Nil fields returned by helpers such as
errorc.Error("cause", nil)are ignored. - Empty field keys print only the value.
- The library requires Go 1.22 or later.
See Installation, Examples, and the companion keys project for more.