A tool for generating error wrappers in Go that provides detailed error context including function name, arguments, and call chain.
- Automatically generates error wrapper types for functions that return errors
- Wraps errors with context about where and why they occurred
- Preserves error chains with
Unwrap(), Is() and As()
support - Includes function arguments in error messages
- Handles methods on structs and package-level functions
- Supports all basic Go types and custom types
- Automatically modifies existing error returns to use wrappers
go install github.com/Bionic2113/errgen@latest
Run in your project directory:
errgen
This will:
- Scan all .go files in the current directory and subdirectories
- Generate error wrapper types for functions that return errors
- Create
errors.go
files in each package containing the wrappers - Modify existing error returns to use the new wrappers
- It can skip function arguments using the
pkg/skipper
package - It can skip structure fields, using the
pkg/stringer
package
See more in example folder
Config:
wrapper_filename: "errwrap_gen"
simple_err_filename: "error_gen"
skipper:
skip_types:
"github.com/Bionic2113/errgen/pkg/skipper":
all: false
names:
- "Config"
with_default: true
rules:
- type: suffix
value: "skip.go"
stringer:
separator: "\\n"
connector: ": "
filename: "strings"
tagname: "errgen"
Given this code:
func ProcessUser(user *User, count int) error {
if err := user.UpdateName("New"); err != nil {
return err
}
return errors.New("processing failed")
}
The generator will create a wrapper and modify the code to:
var ErrExample2 = errors.New("processing failed")
func ProcessUser(user *User, count int) error {
if err := user.UpdateName("New"); err != nil {
return NewProcessUserError(user, count, "user.UpdateName", err)
}
return NewProcessUserError(user, count, "unknown error in ProcessUser", ErrExample2)
}
type ProcessUserError struct {
user *User
count int
reasonErrGen string
errErrGen error
}
func NewProcessUserError(user *User, count int, reasonErrGen string, errErrGen error) *ProcessUserError {
return &ProcessUserError{
user: user,
count: count,
reasonErrGen: reasonErrGen,
errErrGen: errErrGen,
}
}
func (e *ProcessUserError) Error() string {
return "[" + "example" + "] - " +
"ProcessUser - " + e.reasonErrGen +
" - args: {" +
"user: " + fmt.Sprintf("%#v", e.user) + ", " +
"count: " + strconv.Itoa(e.count) +
"}" + "\n" +
e.errErrGen.Error()
}
func (e *ProcessUserError) Unwrap() error {
return e.errErrGen
}
func (e *ProcessUserError) Is(target error) bool {
if _, ok := target.(*ProcessUserError); ok {
return true
}
return errors.Is(e.errErrGen, target)
}
This provides rich error context while maintaining the original error chain.