Go 1.13+ compatible error wrapping with call stacks and function parameters.
- Automatic call stack capture - Every error wrapped with this package includes the full call stack at the point where the error was created or wrapped
- Function parameter tracking - Capture and display function parameters in error messages for detailed debugging
- Go 1.13+ error wrapping compatible - Works seamlessly with
errors.Is,errors.As, anderrors.Unwrap - Zero allocation optimization - Specialized functions for 0-10 parameters to avoid varargs allocations
- Helper utilities - Common patterns for NotFound errors, context errors, and panic recovery
- Customizable formatting - Control how sensitive data appears in error messages
- Go 1.23+ iterator support - Convert errors to iterators for functional programming patterns
go get github.com/domonda/go-errsimport "github.com/domonda/go-errs"
func DoSomething() error {
return errs.New("something went wrong")
}
// Error output includes call stack:
// something went wrong
// main.DoSomething
// /path/to/file.go:123The most powerful feature - automatically capture function parameters when errors occur:
func ProcessUser(userID string, age int) (err error) {
defer errs.WrapWithFuncParams(&err, userID, age)
if age < 0 {
return errors.New("invalid age")
}
return database.UpdateUser(userID, age)
}
// When an error occurs, output includes:
// invalid age
// main.ProcessUser("user-123", -5)
// /path/to/file.go:45func LoadConfig(path string) error {
data, err := os.ReadFile(path)
if err != nil {
return errs.Errorf("failed to read config: %w", err)
}
// ... parse data
return nil
}errs.New(text)- Create a new error with call stackerrs.Errorf(format, ...args)- Format an error with call stack (supports%wfor wrapping)errs.Sentinel(text)- Create a const-able sentinel error
errs.WrapWithFuncParams(&err, params...)- Most common: wrap error with function parameterserrs.WrapWith0FuncParams(&err)througherrs.WrapWith10FuncParams(&err, p0, ...)- Optimized variants for specific parameter countserrs.WrapWithFuncParamsSkip(skip, &err, params...)- Advanced: control stack frame skipping
errs.WrapWithCallStack(err)- Wrap error with call stack onlyerrs.WrapWithCallStackSkip(skip, err)- Advanced: control stack frame skipping
Standardized "not found" error handling compatible with sql.ErrNoRows and os.ErrNotExist:
var ErrUserNotFound = fmt.Errorf("user %w", errs.ErrNotFound)
func GetUser(id string) (*User, error) {
user, err := db.Query("SELECT * FROM users WHERE id = ?", id)
if errs.IsErrNotFound(err) {
return nil, ErrUserNotFound
}
return user, err
}
// Check for any "not found" variant
if errs.IsErrNotFound(err) {
// Handle not found case
}// Check if context is done
if errs.IsContextDone(ctx) {
// Handle context done
}
// Check specific context errors
if errs.IsContextCanceled(ctx) {
// Handle cancellation
}
if errs.IsContextDeadlineExceeded(ctx) {
// Handle timeout
}
// Check if an error is context-related
if errs.IsContextError(err) {
// Don't retry context errors
}func RiskyOperation() (err error) {
defer errs.RecoverPanicAsError(&err)
// If this panics, it will be converted to an error
return doSomethingRisky()
}
// With function parameters
func ProcessItem(id string) (err error) {
defer errs.RecoverPanicAsErrorWithFuncParams(&err, id)
return processItem(id) // May panic
}type CustomError struct {
error
}
func (e CustomError) ShouldLog() bool {
return false // Don't log this error
}
// Check if error should be logged
if errs.ShouldLog(err) {
logger.Error(err)
}
// Wrap error to prevent logging
err = errs.DontLog(err)For simple cases where you want to prevent a parameter from appearing in logs, use errs.KeepSecret(param):
func Login(username string, password string) (err error) {
defer errs.WrapWithFuncParams(&err, username, errs.KeepSecret(password))
// Error messages will show: Login("admin", ***REDACTED***)
return authenticate(username, password)
}The Secret interface wraps a value and ensures it's never logged or printed:
String()returns"***REDACTED***"- Implements
CallStackPrintableto ensure redaction in error call stacks - Use
secret.Secrect()to retrieve the actual value when needed
Important: Wrapping a parameter with errs.KeepSecret() is preferable to omitting it entirely from a defer errs.WrapWith* statement. When you run go-errs-wrap replace, omitted parameters will be added back, but KeepSecret-wrapped parameters are preserved in their wrapped form.
For custom types, implement CallStackPrintable to control how they appear in error messages:
type Password struct {
value string
}
func (p Password) PrintForCallStack(w io.Writer) {
w.Write([]byte("***REDACTED***"))
}
func Login(username string, pwd Password) (err error) {
defer errs.WrapWithFuncParams(&err, username, pwd)
// Error messages will show: Login("admin", ***REDACTED***)
return authenticate(username, pwd)
}// Check if error chain contains a specific type
if errs.Has[*DatabaseError](err) {
// Handle database error
}
// Get all errors of a specific type from the chain
dbErrors := errs.As[*DatabaseError](err)
for _, dbErr := range dbErrors {
// Handle each database error
}
// Check error type without custom Is/As methods
if errs.Type[*DatabaseError](err) {
// Error is or wraps a DatabaseError
}// Get the root cause error
rootErr := errs.Root(err)
// Unwrap call stack information only
plainErr := errs.UnwrapCallStack(err)// Convert error to single-value iterator
for err := range errs.IterSeq(myErr) {
// Process error
}
// Convert error to two-value iterator (value, error) pattern
for val, err := range errs.IterSeq2[MyType](myErr) {
if err != nil {
// Handle error
}
}// Change path prefix trimming
errs.TrimFilePathPrefix = "/go/src/"
// Adjust maximum stack depth
errs.MaxCallStackFrames = 64 // Default is 32// Replace the global formatter
errs.FormatFunctionCall = func(function string, params ...any) string {
// Your custom formatting logic
return fmt.Sprintf("%s(%v)", function, params)
}func MyFunc(id string) (err error) {
defer errs.WrapWithFuncParams(&err, id)
// Function body
}// Instead of:
defer errs.WrapWithFuncParams(&err, p0, p1, p2)
// Use:
defer errs.WrapWith3FuncParams(&err, p0, p1, p2)type APIKey string
func (k APIKey) PrintForCallStack(w io.Writer) {
io.WriteString(w, "***")
}// Good - preserves error chain
return errs.Errorf("failed to process user %s: %w", userID, err)
// Avoid - loses error chain
return errs.New(fmt.Sprintf("failed: %s", err))// Use errs.New instead of errors.New
return errs.New("something failed")
// Use errs.Errorf instead of fmt.Errorf
return errs.Errorf("failed: %w", err)The go-errs-wrap command-line tool helps manage defer errs.WrapWithFuncParams statements in your Go code.
go install github.com/domonda/go-errs/cmd/go-errs-wrap@latest| Command | Description |
|---|---|
remove |
Remove all defer errs.Wrap* or //#wrap-result-err lines |
replace |
Replace existing defer errs.Wrap* or //#wrap-result-err with properly generated code |
insert |
Insert defer errs.Wrap* at the first line of functions with named error results that don't already have one |
Insert wrap statements into all functions missing them:
# Process a single file
go-errs-wrap insert ./pkg/mypackage/file.go
# Process all Go files in a directory recursively
go-errs-wrap insert ./pkg/...Replace outdated wrap statements with correct ones:
# Update parameters in existing wrap statements
go-errs-wrap replace ./pkg/mypackage/file.goRemove all wrap statements:
go-errs-wrap remove ./pkg/...Write changes to another output location:
# Output to a different location
go-errs-wrap insert -out ./output ./pkg/mypackage
# Show verbose progress
go-errs-wrap insert -verbose ./pkg/...| Option | Description |
|---|---|
-out <path> |
Output to different location instead of modifying source |
-minvariadic |
Use specialized WrapWithNFuncParams functions instead of variadic |
-verbose |
Print progress information |
-help |
Show help message |
Given this input file:
package example
func ProcessData(ctx context.Context, id string) (err error) {
return doWork(ctx, id)
}Running go-errs-wrap insert example.go produces:
package example
import "github.com/domonda/go-errs"
func ProcessData(ctx context.Context, id string) (err error) {
defer errs.WrapWith2FuncParams(&err, ctx, id)
return doWork(ctx, id)
}The tool:
- Inserts the
defer errs.Wrap*statement at the first line of the function body - Adds an empty line after the defer statement
- Automatically adds the required import
- Uses the optimized function variant based on parameter count
- Skips functions without named error results
- Skips functions that already have a
defer errs.Wrap*statement - Preserves
errs.KeepSecret(param)wrapped parameters during replacement
When using go-errs-wrap replace, parameters wrapped with errs.KeepSecret() are preserved. This allows developers to mark sensitive parameters once and have that protection maintained across replacements:
// Before: developer manually wrapped password with KeepSecret
func Login(username, password string) (err error) {
defer errs.WrapWithFuncParams(&err, username, errs.KeepSecret(password))
return authenticate(username, password)
}
// After running: go-errs-wrap replace -minvariadic file.go
// The KeepSecret wrapping is preserved:
func Login(username, password string) (err error) {
defer errs.WrapWith2FuncParams(&err, username, errs.KeepSecret(password))
return authenticate(username, password)
}This is preferable to omitting sensitive parameters entirely, as omitted parameters would be re-added by the tool.
- Go version: Requires Go 1.13+ for error wrapping, Go 1.23+ for iterator support
- Error handling: Fully compatible with
errors.Is,errors.As,errors.Unwrap, anderrors.Join - Testing: Use with
testifyor any testing framework
- Zero-allocation error wrapping for functions with 0-10 parameters (using specialized functions)
- Efficient call stack capture using
runtime.Callers - Lazy error message formatting - only formats when
Error()is called - Configurable stack depth to balance detail vs memory usage
See the examples directory and godoc for more examples.
MIT License - see LICENSE file for details.
Contributions welcome! Please open an issue or submit a pull request.
- go-pretty - Pretty printing used for error parameter formatting