Documentation
¶
Overview ¶
Package errutil provides small, reusable helpers for consistent error handling in Go applications.
Many Go services repeat the same error-handling patterns: appending cleanup errors, enriching failures with call-site context, and preserving compatibility with the standard errors API. This package centralizes those patterns so teams can keep error paths concise and predictable.
Top features:
- Trace annotates an error with runtime caller metadata (file, line, function) while preserving the original error with %w wrapping. This gives developers immediate debugging context without losing errors.Is/errors.As behavior.
- JoinFnError executes an error-producing function and joins its result into an existing error value using errors.Join. This is useful for defer/cleanup logic where secondary failures must not overwrite the primary error.
- Errors enumerates the individual errors aggregated within an errors.Join value, returning a single-element slice for a plain error and nil for nil.
- Nil-safe behavior: Trace(nil) returns nil, and JoinFnError naturally supports nil and non-nil combinations through errors.Join semantics.
Benefits:
- reduces boilerplate in error-return and defer paths
- improves observability and diagnosis of production failures
- encourages idiomatic Go error composition based on errors.Join and %w
Index ¶
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var ErrNilErrorFunc = errors.New("errutil: nil ErrorFunc")
ErrNilErrorFunc is joined into the target error by JoinFnError when the supplied ErrorFunc is nil, surfacing the programming mistake without panicking.
Functions ¶
func Errors ¶
Errors returns the individual errors aggregated within err.
It lets callers enumerate the parts of an aggregate produced by errors.Join:
- a nil err yields a nil slice;
- an err implementing Unwrap() []error (such as the value returned by errors.Join) yields a copy of its aggregated errors;
- any other non-nil err yields a single-element slice holding err.
The returned slice never aliases the internal storage of the aggregate error, so callers may retain and modify it freely.
func JoinFnError ¶
JoinFnError executes fn and joins its error into *err.
This helper is intended for defer/cleanup paths where secondary failures should be preserved without overwriting a primary error. It is typically used as:
func do() (err error) {
f, err := open()
if err != nil {
return err
}
defer errutil.JoinFnError(&err, f.Close)
// ...
}
err must point at the error that will ultimately be returned, usually the address of a named return value; passing the address of a local variable that is not returned silently discards the joined error.
If err is nil the call is a no-op. If fn is nil, ErrNilErrorFunc is joined into *err. The primary error is left untouched when fn returns nil.
Example ¶
package main
import (
"errors"
"fmt"
"github.com/tecnickcom/nurago/pkg/errutil"
)
func main() {
err := errors.New("original error")
fn := func() error {
return errors.New("function error")
}
errutil.JoinFnError(&err, fn)
fmt.Println(err)
}
Output: original error function error
Example (Defer) ¶
package main
import (
"errors"
"fmt"
"github.com/tecnickcom/nurago/pkg/errutil"
)
func main() {
// JoinFnError is designed for defer/cleanup: err must be a named return
// value so the deferred call can amend it after the function body runs.
process := func() (err error) { //nolint:nonamedreturns // JoinFnError amends the return via its address
closer := func() error {
return errors.New("close failed")
}
defer errutil.JoinFnError(&err, closer)
return errors.New("primary failure")
}
fmt.Println(process())
}
Output: primary failure close failed
func Trace ¶
Trace wraps err with caller file, line, and function metadata.
It returns nil when err is nil. The original error is wrapped with %w so errors.Is and errors.As continue to work. When caller metadata cannot be recovered, the function name falls back to "unknown".
The file path is the one recorded by the compiler: it is absolute for a plain build and module-relative when the binary is built with -trimpath. Avoid relying on its exact form and prefer -trimpath for release builds to keep build-host paths out of error messages and logs.
Example ¶
package main
import (
"errors"
"fmt"
"github.com/tecnickcom/nurago/pkg/errutil"
)
func main() {
err := errors.New("example error")
testErr := errutil.Trace(err)
fmt.Println(testErr)
}
Output: