Documentation ¶
Overview ¶
Package errors is a drop-in replacement and extension of the Go standard library's package errors.
Index ¶
- Variables
- func Annotate(err error, format string, args ...any) (annotated error)
- func As(err error, target any) (ok bool)
- func Check(err error)
- func FromRecovered(v any) (err error)
- func Is(err, target error) (ok bool)
- func Join(errs ...error) error
- func Must[T any](v T, err error) (res T)
- func New(msg string) (err error)deprecated
- func Unwrap(err error) (unwrapped error)
- func WithDeferred(returned, deferred error) (result error)
- type Aser
- type Deferred
- type Error
- type Iser
- type Pair
- type Wrapper
- type WrapperSlice
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var ErrUnsupported = stderrors.ErrUnsupported
ErrUnsupported indicates that a requested operation cannot be performed, because it is unsupported. For example, a call to os.Link when using a file system that does not support hard links.
Functions ¶
func Annotate ¶
Annotate annotates the error with the message, unless the error is nil. The last verb in format must be a verb compatible with errors, for example "%w".
In Defers ¶
The primary use case for this function is to simplify code like this:
func (f *foo) doStuff(s string) (err error) { defer func() { if err != nil { err = fmt.Errorf("bad foo %q: %w", s, err) } }() // … }
Instead, write:
func (f *foo) doStuff(s string) (err error) { defer func() { err = errors.Annotate(err, "bad foo %q: %w", s) }() // … }
At The End Of Functions ¶
Another possible use case is to simplify final checks like this:
func (f *foo) doStuff(s string) (err error) { // … if err != nil { return fmt.Errorf("doing stuff with %s: %w", s, err) } return nil }
Instead, you could write:
func (f *foo) doStuff(s string) (err error) { // … return errors.Annotate(err, "doing stuff with %s: %w", s) }
Warning ¶
This function requires that there be only ONE error named "err" in the function and that it is always the one that is returned. Example (Bad) provides an example of the incorrect usage of Annotate.
Example ¶
package main import ( "fmt" "github.com/AdguardTeam/golibs/errors" ) func main() { f := func(fn string) (err error) { defer func() { err = errors.Annotate(err, "reading %q: %w", fn) }() return errors.Error("not found") } err := f("non-existing") fmt.Println("with err :", err) f = func(fn string) (err error) { defer func() { err = errors.Annotate(err, "reading %q: %w", fn) }() return nil } err = f("non-existing") fmt.Println("without err :", err) }
Output: with err : reading "non-existing": not found without err : <nil>
Example (Bad) ¶
package main import ( "fmt" "github.com/AdguardTeam/golibs/errors" ) func main() { const errNotFound errors.Error = "not found" cond := true g := func() (err error) { return nil } f := func() error { if cond { err := g() if err != nil { return err } // BAD! This err is not the same err as the one that is // returned from the top level. defer func() { err = errors.Annotate(err, "f") }() } // This error is returned without an annotation. return errNotFound } // Outputs the error without an annotation. fmt.Println(f()) }
Output: not found
Example (End) ¶
package main import ( "fmt" "github.com/AdguardTeam/golibs/errors" ) func main() { f := func(fn string) (err error) { err = errors.Error("not found") // Some operations. return errors.Annotate(err, "reading %q: %w", fn) } err := f("non-existing") fmt.Println("with err :", err) f = func(fn string) (err error) { // Some operations. return errors.Annotate(err, "reading %q: %w", fn) } err = f("non-existing") fmt.Println("without err :", err) }
Output: with err : reading "non-existing": not found without err : <nil>
func As ¶
As finds the first error in err's chain that matches target, and if so, sets target to that error value and returns true. Otherwise, it returns false.
It calls stderrors.As from the Go standard library.
Example ¶
package main import ( "fmt" "os" "github.com/AdguardTeam/golibs/errors" ) func main() { if _, err := os.Open("non-existing"); err != nil { var pathError *os.PathError if errors.As(err, &pathError) { fmt.Println("Failed at path:", pathError.Path) } else { fmt.Println(err) } } }
Output: Failed at path: non-existing
func Check ¶ added in v0.26.0
func Check(err error)
Check is a simple error-checking helper that panics if err is not nil. It must only be used within main/entrypoint functions or in simple scripts.
Example ¶
package main import ( "fmt" "github.com/AdguardTeam/golibs/errors" ) func main() { defer func() { fmt.Println("recovered:", errors.FromRecovered(recover())) }() runFoo := func() (err error) { return errors.Error("test error") } errors.Check(runFoo()) }
Output: recovered: test error
func FromRecovered ¶ added in v0.26.0
FromRecovered checks if v, which should be a value returned by recover) is an error and, if it isn't, wraps it using fmt.Errorf. If v is nil, err is nil.
Example ¶
package main import ( "fmt" "github.com/AdguardTeam/golibs/errors" ) func main() { printRecovered := func() { err := errors.FromRecovered(recover()) if err != nil { fmt.Printf("got error: %T(%[1]s)\n", err) } else { fmt.Println("no errors") } } func() { defer printRecovered() // No panic. }() func() { defer printRecovered() // Panic with error. panic(errors.Error("test error")) }() func() { defer printRecovered() // Panic with value. panic("test error") }() }
Output: no errors got error: errors.Error(test error) got error: *errors.errorString(recovered: test error)
func Is ¶
Is reports whether any error in err's chain matches target.
It calls stderrors.Is from the Go standard library.
Example ¶
package main import ( "fmt" "os" "github.com/AdguardTeam/golibs/errors" ) func main() { if _, err := os.Open("non-existing"); err != nil { if errors.Is(err, os.ErrNotExist) { fmt.Println("file does not exist") } else { fmt.Println(err) } } }
Output: file does not exist
func Join ¶ added in v0.12.1
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.
It calls stderrors.Join from the Go standard library.
Example ¶
package main import ( "fmt" "github.com/AdguardTeam/golibs/errors" ) func main() { err1 := errors.New("err1") err2 := errors.New("err2") err := errors.Join(err1, err2) fmt.Println(err) if errors.Is(err, err1) { fmt.Println("err is err1") } if errors.Is(err, err2) { fmt.Println("err is err2") } }
Output: err1 err2 err is err1 err is err2
func Must ¶ added in v0.26.0
Must is a helper that wraps a call to a function returning (T, error) and panics if the error is non-nil. It must only be used within main/entrypoint functions, in simple scripts, and in variable initializations such as:
var testAddr = errors.Must(parseAddr("addr_value"))
If an appropriate function already exists, for example [netip.MustParseAddr], Must should not be used.
Example ¶
package main import ( "fmt" "github.com/AdguardTeam/golibs/errors" ) func main() { newFooBad := func() (foo string, err error) { return "", errors.Error("test error") } newFooGood := func() (foo string, err error) { return "foo", nil } func() { fmt.Println("good:", errors.Must(newFooGood())) }() func() { defer func() { fmt.Println("bad:", errors.FromRecovered(recover())) }() fmt.Println(errors.Must(newFooBad())) }() }
Output: good: foo bad: test error
func New
deprecated
New returns an error that formats as the given msg. Each call to New returns a distinct error value even if the text is identical.
It calls stderrors.New from the Go standard library.
Deprecated: Use type Error and constant errors instead.
Example ¶
package main import ( "fmt" "github.com/AdguardTeam/golibs/errors" ) func main() { err := errors.New("emit macho dwarf: elf header corrupted") if err != nil { fmt.Print(err) } }
Output: emit macho dwarf: elf header corrupted
func Unwrap ¶
Unwrap returns the result of calling the Unwrap method on err, if err's type contains an Unwrap method returning error. Otherwise, Unwrap returns nil.
It calls stderrors.Unwrap from the Go standard library.
func WithDeferred ¶
WithDeferred is a helper function for deferred errors. For example, to preserve errors from the Close method, replace this:
defer f.Close()
With this:
defer func() { err = errors.WithDeferred(err, f.Close()) }
If returned is nil and deferred is non-nil, the returned error implements the Deferred interface. If both returned and deferred are non-nil, result has the underlying type of *Pair.
Warning ¶
This function requires that there be only ONE error named "err" in the function and that it is always the one that is returned. Example (Bad) provides an example of the incorrect usage of WithDeferred.
Example ¶
package main import ( "fmt" "github.com/AdguardTeam/golibs/errors" ) func main() { const ( errClose errors.Error = "close fail" errNotFound errors.Error = "not found" ) closeFunc := func(fn string) (err error) { return nil } f := func(fn string) (err error) { defer func() { err = errors.WithDeferred(err, closeFunc(fn)) }() return nil } err := f("non-existing") fmt.Println("without errs :", err) closeFunc = func(fn string) (err error) { return nil } f = func(fn string) (err error) { defer func() { err = errors.WithDeferred(err, closeFunc(fn)) }() return errNotFound } err = f("non-existing") fmt.Println("with returned err :", err) closeFunc = func(fn string) (err error) { return errClose } f = func(fn string) (err error) { defer func() { err = errors.WithDeferred(err, closeFunc(fn)) }() return nil } err = f("non-existing") fmt.Println("with deferred err :", err) closeFunc = func(_ string) (err error) { return errClose } f = func(fn string) (err error) { defer func() { err = errors.WithDeferred(err, closeFunc(fn)) }() return errNotFound } err = f("non-existing") fmt.Println("with both errs :", err) }
Output: without errs : <nil> with returned err : not found with deferred err : deferred: close fail with both errs : returned: "not found", deferred: "close fail"
Example (Bad) ¶
package main import ( "fmt" "github.com/AdguardTeam/golibs/errors" ) func main() { const ( errClose errors.Error = "close fail" errNotFound errors.Error = "not found" ) cond := true closeFunc := func(_ string) (err error) { return errClose } g := func() (err error) { return nil } f := func(fn string) error { if cond { err := g() if err != nil { return err } // BAD! This err is not the same err as the one that is // returned from the top level. defer func() { err = errors.WithDeferred(err, closeFunc(fn)) }() } // This error is the one that is actually returned. return errNotFound } // Only outputs the returned error and ignores the deferred one. fmt.Println(f("non-existing")) }
Output: not found
Types ¶
type Aser ¶
Aser is a copy of the hidden aser interface from the Go standard library. It is added here for tests, linting, etc.
type Deferred ¶
Deferred is the interface for errors that were returned by cleanup functions, such as Close. This is useful in APIs which desire to handle such errors differently, for example to log them as warnings.
Method Deferred returns a bool to mirror the behavior of types like net.Error and allow implementations to decide if the error is a deferred one dynamically. Users of this API must check its return value as well as the result errors.As.
if errDef := errors.Deferred(nil); errors.As(err, &errDef) && errDef.Deferred() { // … }
See https://dave.cheney.net/2014/12/24/inspecting-errors.
Example ¶
package main import ( "fmt" "github.com/AdguardTeam/golibs/errors" ) func main() { const ( errClose errors.Error = "close fail" errNotFound errors.Error = "not found" ) // logErr logs the error according to its severity level. logErr := func(err error) { if defErr := errors.Deferred(nil); errors.As(err, &defErr) && defErr.Deferred() { // Log deferred errors as warnings. fmt.Printf("warning: %s\n", errors.Unwrap(defErr)) } else { fmt.Printf("ERROR: %s\n", err) } } // Case 1: the function fails, but the cleanup succeeds. closeFunc := func(fn string) (err error) { return nil } f := func(fn string) (err error) { defer func() { err = errors.WithDeferred(err, closeFunc(fn)) }() return errNotFound } err := f("non-existing") logErr(err) // Case 2: the function succeeds, but the cleanup fails. closeFunc = func(_ string) (err error) { return errClose } f = func(fn string) (err error) { defer func() { err = errors.WithDeferred(err, closeFunc(fn)) }() return nil } err = f("non-existing") logErr(err) }
Output: ERROR: not found warning: close fail
type Error ¶
type Error string
Error is the constant error type.
See https://dave.cheney.net/2016/04/07/constant-errors.
Example ¶
package main import ( "fmt" "github.com/AdguardTeam/golibs/errors" ) func main() { const errNotFound errors.Error = "not found" f := func(fn string) (err error) { return fmt.Errorf("opening %q: %w", fn, errNotFound) } err := f("non-existing") fmt.Println("err :", err) fmt.Println("unwrapped :", errors.Unwrap(err)) }
Output: err : opening "non-existing": not found unwrapped : not found
const ErrBadEnumValue Error = "bad enum value"
ErrBadEnumValue indicates that the provided value is not a valid value within an enumeration of types (a sum type) or values.
For a value that is outside of a range of ordered values, use ErrOutOfRange.
const ErrEmptyValue Error = "empty value"
ErrEmptyValue indicates that a value is provided but it is empty. For example, a non-null but empty JSON or YAML object.
For an absent value, use ErrNoValue.
const ErrNegative Error = "negative value"
ErrNegative indicates that the provided value is negative when it should be greater than or equal to zero.
For a value that should be greater than zero, use ErrNotPositive.
const ErrNoValue Error = "no value"
ErrNoValue indicates that a required value has not been provided. For example, a null instead of an object in a JSON or YAML document.
For a value that is present but empty, use ErrEmptyValue.
const ErrNotPositive Error = "not positive"
ErrNotPositive indicates that the provided value is negative or zero when it should be greater than zero.
For a value that should be greater than or equal to zero, use ErrNegative.
const ErrOutOfRange Error = "out of range"
ErrOutOfRange indicates that provided value is outside of a valid range of ordered values.
For a value that is not a valid enum or sum type value, use ErrBadEnumValue.
type Iser ¶
Iser is a copy of the hidden iser interface from the Go standard library. It is added here for tests, linting, etc.
type Pair ¶
Pair is a pair of errors. The Returned error is the main error that has been returned by a function. The Deferred error is the error returned by the cleanup function, such as io.Closer.Close.
In pairs returned from WithDeferred, the Deferred error always implements the Deferred interface.
Example ¶
package main import ( "fmt" "github.com/AdguardTeam/golibs/errors" ) func main() { closeFunc := func(_ string) (err error) { return errors.Error("close fail") } f := func(fn string) (err error) { defer func() { err = errors.WithDeferred(err, closeFunc(fn)) }() return errors.Error("not found") } err := f("non-existing") fmt.Println("err :", err) fmt.Println("unwrapped :", errors.Unwrap(err)) fmt.Println() errPair := &errors.Pair{} if !errors.As(err, &errPair) { panic("err is not an *error.Pair") } defErr := errPair.Deferred.(errors.Deferred) fmt.Println("deferred :", defErr) fmt.Println("deferred unwrapped :", errors.Unwrap(defErr)) fmt.Println("deferred check :", defErr.Deferred()) }
Output: err : returned: "not found", deferred: "close fail" unwrapped : not found deferred : deferred: close fail deferred unwrapped : close fail deferred check : true
type Wrapper ¶
type Wrapper interface {
Unwrap() error
}
Wrapper is a copy of the hidden wrapper interface from the Go standard library. It is added here for tests, linting, etc.
type WrapperSlice ¶ added in v0.12.1
type WrapperSlice interface {
Unwrap() []error
}
WrapperSlice is a copy of the hidden wrapper interface added to the Go standard library in Go 1.20. It is added here for tests, linting, etc.