Documentation ¶
Overview ¶
Package errors provides enhanced error handling primitives that extend Go's standard errors package. It offers stack traces, error wrapping, and HTTP error handling while maintaining compatibility with the standard library.
This is a copy of github.com/pkg/errors slightly updated and extended to support more modern logging approaches.
Key Features:
- Stack Traces: Automatically capture stack traces when errors are created
- Error Wrapping: Add context to errors while preserving the original error
- HTTP Errors: Structured error types for HTTP responses
- Error Joining: Combine multiple errors into a single error value
Basic Usage:
// Create a new error with stack trace err := errors.New("database connection failed") // Add context to an existing error if err != nil { return errors.Wrap(err, "failed to initialize storage") } // Format error with stack trace fmt.Printf("%+v\n", err)
Error Types:
fundamental - Base error type with stack trace withStack - Adds stack trace to an existing error withMessage - Adds a message to an existing error withMultierr - Combines multiple errors into one HTTPError - HTTP-specific error with status code
Error Wrapping:
The package provides several ways to wrap errors:
errors.Wrap(err, "message") // Add message and stack trace errors.Wrapf(err, "fmt", args) // Add formatted message and stack trace errors.WithStack(err) // Add only stack trace errors.WithMessage(err, "msg") // Add only message
Error Inspection:
Use these methods to inspect errors:
errors.Is(err, target) // Check if err matches target errors.As(err, &target) // Try to convert err to target type errors.Cause(err) // Get the root cause of the error
HTTP Error Handling:
For HTTP services, use the HTTP error types:
errors.NewHTTPError(http.StatusNotFound, "user not found", err) errors.AsHTTPError(err) // Convert any error to an HTTPError
Formatting:
All error types implement fmt.Formatter and support:
%s - Print the error message %v - Same as %s %q - Print the error message with quotes %+v - Print detailed error with stack trace
Error Joining:
Combine multiple errors into one:
err1 := errors.New("first error") err2 := errors.New("second error") combined := errors.Join(err1, err2)
Best Practices:
- Always use errors.New() instead of errors.New() for new errors
- Use errors.Wrap() when adding context to returned errors
- Use %+v when logging errors to include stack traces
- Use errors.Is() and errors.As() for error inspection
- Use HTTPError types for HTTP API responses
Example (StackTrace) ¶
package main import ( "fmt" "github.com/aexvir/skladka/internal/errors" ) func fn() error { e1 := errors.New("error") e2 := errors.Wrap(e1, "inner") e3 := errors.Wrap(e2, "middle") return errors.Wrap(e3, "outer") } func main() { type stackTracer interface { StackTrace() errors.StackTrace } err, ok := errors.Cause(fn()).(stackTracer) if !ok { panic("oops, err does not implement stackTracer") } st := err.StackTrace() fmt.Printf("%+v", st[0:2]) // top two frames // Example output: // github.com/pkg/errors_test.fn // /home/dfc/src/github.com/pkg/errors/example_test.go:47 // github.com/pkg/errors_test.Example_stackTrace // /home/dfc/src/github.com/pkg/errors/example_test.go:127 }
Output:
Index ¶
- func As(err error, target any) bool
- func Cause(err error) error
- func Errorf(format string, args ...interface{}) error
- func Is(err, target error) bool
- func Join(errs ...error) error
- func New(message string) error
- func NewHTTPError(code int, message string, err error) error
- func WithErrorHandler(handler ErrorHandlerFunc) http.HandlerFunc
- func WithMessage(err error, message string) error
- func WithMessagef(err error, format string, args ...interface{}) error
- func WithStack(err error) error
- func Wrap(err error, message string) error
- func Wrapf(err error, format string, args ...interface{}) error
- type ErrorHandlerFunc
- type Frame
- type HTTPError
- type StackTrace
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func As ¶
As finds the first error in err's chain that matches target, and if one is found, As returns true and sets target to that error value. Otherwise, As returns false and sets target to nil.
func Cause ¶
Cause returns the underlying cause of the error, if possible. An error value has a cause if it implements the following interface:
type causer interface { Cause() error }
If the error does not implement Cause, the original error will be returned. If the error is nil, nil will be returned without further investigation.
Example ¶
package main import ( "fmt" "github.com/aexvir/skladka/internal/errors" ) func fn() error { e1 := errors.New("error") e2 := errors.Wrap(e1, "inner") e3 := errors.Wrap(e2, "middle") return errors.Wrap(e3, "outer") } func main() { err := fn() fmt.Println(err) fmt.Println(errors.Cause(err)) }
Output: outer: middle: inner: error error
Example (Printf) ¶
package main import ( "fmt" "github.com/aexvir/skladka/internal/errors" ) func main() { err := errors.Wrap(func() error { return func() error { return errors.New("hello world") }() }(), "failed") fmt.Printf("%v", err) }
Output: failed: hello world
func Errorf ¶
Errorf returns a new error with formatted message and a stack trace. The error implements fmt.Formatter for custom error formatting.
name := "db" err := errors.Errorf("connection to %s failed", name)
Example (Extended) ¶
package main import ( "fmt" "github.com/aexvir/skladka/internal/errors" ) func main() { err := errors.Errorf("whoops: %s", "foo") fmt.Printf("%+v", err) // Example output: // whoops: foo // github.com/pkg/errors_test.ExampleErrorf // /home/dfc/src/github.com/pkg/errors/example_test.go:101 // testing.runExample // /home/dfc/go/src/testing/example.go:114 // testing.RunExamples // /home/dfc/go/src/testing/example.go:38 // testing.(*M).Run // /home/dfc/go/src/testing/testing.go:744 // main.main // /github.com/pkg/errors/_test/_testmain.go:102 // runtime.main // /home/dfc/go/src/runtime/proc.go:183 // runtime.goexit // /home/dfc/go/src/runtime/asm_amd64.s:2059 }
Output:
func Is ¶
Is reports whether any error in errs matches target. An error matches target if the error values are identical, as determined by ==.
func Join ¶
Join returns an error that wraps the given errors. Any nil error values are discarded. Join returns nil if errs contains no non-nil values. The error formats as the concatenation of the strings obtained by calling the Error method of each element of errs, with a newline between each string.
err1 := errors.New("first error") err2 := errors.New("second error") err := errors.Join(err1, err2) fmt.Printf("%v", err) // Output: // first error // second error
Example ¶
package main import ( "fmt" "github.com/aexvir/skladka/internal/errors" ) func main() { err1 := errors.New("error 1") err2 := errors.Wrap(errors.New("inner"), "error 2") err := errors.Join(err1, err2) fmt.Printf("%v\n", err) }
Output: error 1 error 2: inner
func New ¶
New returns a new error with a stack trace. The error implements fmt.Formatter for custom error formatting.
err := errors.New("connection failed") fmt.Printf("%+v", err) // prints error with stack trace
Example ¶
package main import ( "fmt" "github.com/aexvir/skladka/internal/errors" ) func main() { err := errors.New("whoops") fmt.Println(err) }
Output: whoops
Example (Printf) ¶
package main import ( "fmt" "github.com/aexvir/skladka/internal/errors" ) func main() { err := errors.New("whoops") fmt.Printf("%+v", err) // Example output: // whoops // github.com/pkg/errors_test.ExampleNew_printf // /home/dfc/src/github.com/pkg/errors/example_test.go:17 // testing.runExample // /home/dfc/go/src/testing/example.go:114 // testing.RunExamples // /home/dfc/go/src/testing/example.go:38 // testing.(*M).Run // /home/dfc/go/src/testing/testing.go:744 // main.main // /github.com/pkg/errors/_test/_testmain.go:106 // runtime.main // /home/dfc/go/src/runtime/proc.go:183 // runtime.goexit // /home/dfc/go/src/runtime/asm_amd64.s:2059 }
Output:
func NewHTTPError ¶
NewHTTPError creates a new HTTPError with the given code and message. If err is not nil, it will be wrapped and accessible via Unwrap().
err := errors.NewHTTPError(http.StatusNotFound, "user not found", nil) err = errors.NewHTTPError(http.StatusInternalServerError, "database error", dbErr)
func WithErrorHandler ¶
func WithErrorHandler(handler ErrorHandlerFunc) http.HandlerFunc
WithErrorHandler wraps an ErrorHandlerFunc and returns an http.HandlerFunc. It handles errors returned by the ErrorHandlerFunc by:
- For HTTPError: responding with the error's code and message
- For other errors: setting the OpenTelemetry span status to Error and responding with 500 Internal Server Error
Example usage:
http.HandleFunc( "/users", errors.WithErrorHandler( func(w http.ResponseWriter, r *http.Request) error { user, err := getUser(r.Context()) if err != nil { return errors.NewHTTPError(http.StatusNotFound, "user not found", err) } return json.NewEncoder(w).Encode(user) } ) )
func WithMessage ¶
WithMessage annotates err with a new message. If err is nil, WithMessage returns nil.
err := doSomething() if err != nil { return errors.WithMessage(err, "failed to do something") }
Example ¶
package main import ( "fmt" "github.com/aexvir/skladka/internal/errors" ) func main() { cause := errors.New("whoops") err := errors.WithMessage(cause, "oh noes") fmt.Println(err) }
Output: oh noes: whoops
func WithMessagef ¶
WithMessagef annotates err with the format specifier. If err is nil, WithMessagef returns nil.
err := doSomething() if err != nil { return errors.WithMessagef(err, "failed to do something: %s", details) }
func WithStack ¶
WithStack annotates err with a stack trace at the point WithStack was called. If err is nil, WithStack returns nil.
if err != nil { return errors.WithStack(err) }
Example ¶
package main import ( "fmt" "github.com/aexvir/skladka/internal/errors" ) func main() { cause := errors.New("whoops") err := errors.WithStack(cause) fmt.Println(err) }
Output: whoops
Example (Printf) ¶
package main import ( "fmt" "github.com/aexvir/skladka/internal/errors" ) func main() { cause := errors.New("whoops") err := errors.WithStack(cause) fmt.Printf("%+v", err) // Example Output: // whoops // github.com/pkg/errors_test.ExampleWithStack_printf // /home/fabstu/go/src/github.com/pkg/errors/example_test.go:55 // testing.runExample // /usr/lib/go/src/testing/example.go:114 // testing.RunExamples // /usr/lib/go/src/testing/example.go:38 // testing.(*M).Run // /usr/lib/go/src/testing/testing.go:744 // main.main // github.com/pkg/errors/_test/_testmain.go:106 // runtime.main // /usr/lib/go/src/runtime/proc.go:183 // runtime.goexit // /usr/lib/go/src/runtime/asm_amd64.s:2086 // github.com/pkg/errors_test.ExampleWithStack_printf // /home/fabstu/go/src/github.com/pkg/errors/example_test.go:56 // testing.runExample // /usr/lib/go/src/testing/example.go:114 // testing.RunExamples // /usr/lib/go/src/testing/example.go:38 // testing.(*M).Run // /usr/lib/go/src/testing/testing.go:744 // main.main // github.com/pkg/errors/_test/_testmain.go:106 // runtime.main // /usr/lib/go/src/runtime/proc.go:183 // runtime.goexit // /usr/lib/go/src/runtime/asm_amd64.s:2086 }
Output:
func Wrap ¶
Wrap returns an error annotating err with a stack trace at the point Wrap is called, and the supplied message. If err is nil, Wrap returns nil.
err := doSomething() if err != nil { return errors.Wrap(err, "failed to do something") }
Example ¶
package main import ( "fmt" "github.com/aexvir/skladka/internal/errors" ) func main() { cause := errors.New("whoops") err := errors.Wrap(cause, "oh noes") fmt.Println(err) }
Output: oh noes: whoops
Example (Extended) ¶
package main import ( "fmt" "github.com/aexvir/skladka/internal/errors" ) func fn() error { e1 := errors.New("error") e2 := errors.Wrap(e1, "inner") e3 := errors.Wrap(e2, "middle") return errors.Wrap(e3, "outer") } func main() { err := fn() fmt.Printf("%+v\n", err) // Example output: // error // github.com/pkg/errors_test.fn // /home/dfc/src/github.com/pkg/errors/example_test.go:47 // github.com/pkg/errors_test.ExampleCause_printf // /home/dfc/src/github.com/pkg/errors/example_test.go:63 // testing.runExample // /home/dfc/go/src/testing/example.go:114 // testing.RunExamples // /home/dfc/go/src/testing/example.go:38 // testing.(*M).Run // /home/dfc/go/src/testing/testing.go:744 // main.main // /github.com/pkg/errors/_test/_testmain.go:104 // runtime.main // /home/dfc/go/src/runtime/proc.go:183 // runtime.goexit // /home/dfc/go/src/runtime/asm_amd64.s:2059 // github.com/pkg/errors_test.fn // /home/dfc/src/github.com/pkg/errors/example_test.go:48: inner // github.com/pkg/errors_test.fn // /home/dfc/src/github.com/pkg/errors/example_test.go:49: middle // github.com/pkg/errors_test.fn // /home/dfc/src/github.com/pkg/errors/example_test.go:50: outer }
Output:
func Wrapf ¶
Wrapf returns an error annotating err with a stack trace at the point Wrapf is called, and the format specifier. If err is nil, Wrapf returns nil.
err := doSomething() if err != nil { return errors.Wrapf(err, "failed to do something: %s", details) }
Example ¶
package main import ( "fmt" "github.com/aexvir/skladka/internal/errors" ) func main() { cause := errors.New("whoops") err := errors.Wrapf(cause, "oh noes #%d", 2) fmt.Println(err) }
Output: oh noes #2: whoops
Types ¶
type ErrorHandlerFunc ¶
type ErrorHandlerFunc func(http.ResponseWriter, *http.Request) error
type Frame ¶
type Frame uintptr
Frame represents a program counter inside a stack frame. For historical reasons if Frame is interpreted as a uintptr its value represents the program counter + 1.
func (Frame) Format ¶
Format formats the frame according to the fmt.Formatter interface.
%s source file %d source line %n function name %v equivalent to %s:%d
Format accepts flags that alter the printing of some verbs, as follows:
%+s function name and path of source file relative to the compile time GOPATH separated by \n\t (<funcname>\n\t<path>) %+v equivalent to %+s:%d
func (Frame) MarshalText ¶
MarshalText formats a stacktrace Frame as a text string. The output is the same as that of fmt.Sprintf("%+v", f), but without newlines or tabs.
type HTTPError ¶
type HTTPError struct { Code int `json:"code"` Message string `json:"message"` // contains filtered or unexported fields }
HTTPError represents an error that includes an HTTP status code. It implements error, fmt.Formatter, and errors.Unwrap interfaces. The error can be formatted with %s, %q, %v, and %+v verbs.
Example usage:
err := errors.NewHTTPError(http.StatusNotFound, "user not found", nil) fmt.Printf("%+v\n", err) // prints detailed error with stack trace fmt.Printf("%v\n", err) // prints "user not found" fmt.Printf("%q\n", err) // prints "\"user not found\""
func AsHTTPError ¶
AsHTTPError attempts to convert an error to an HTTPError. If the error is already an HTTPError, it is returned as is. If the error is not an HTTPError, it is wrapped as an internal server error.
err := someFunction() httpErr := errors.AsHTTPError(err) fmt.Printf("Status code: %d\n", httpErr.Code)
func (*HTTPError) Format ¶
Format implements the fmt.Formatter interface to customize how the error is formatted. It supports the following format verbs:
%s prints just the error message %q wraps the error message in quotes %v same as %s %+v prints error message, wrapped error if any, and stack trace
func (*HTTPError) Is ¶
Is reports whether this error matches target. An error matches if both the Code and Message are equal.
func (HTTPError) StackTrace ¶
func (s HTTPError) StackTrace() StackTrace
type StackTrace ¶
type StackTrace []Frame
StackTrace is stack of Frames from innermost (newest) to outermost (oldest).
func (StackTrace) Format ¶
func (st StackTrace) Format(state fmt.State, verb rune)
Format formats the stack of Frames according to the fmt.Formatter interface.
%s lists source files for each Frame in the stack %v lists the source file and line number for each Frame in the stack
Format accepts flags that alter the printing of some verbs, as follows:
%+v Prints filename, function, and line number for each Frame in the stack.