There are few options for declaring errors. Consider the following before picking the option best suited for your use case.
- Does the caller need to match the error so that they can handle it?
If yes, we must support the
errors.Is
orerrors.As
functions by declaring a top-level error variable or a custom type. - Is the error message a static string,
or is it a dynamic string that requires contextual information?
For the former, we can use
errors.New
, but for the latter we must usefmt.Errorf
or a custom error type. - Are we propagating a new error returned by a downstream function? If so, see the section on error wrapping.
Error matching? | Error Message | Guidance |
---|---|---|
No | static | errors.New |
No | dynamic | fmt.Errorf |
Yes | static | top-level var with errors.New |
Yes | dynamic | custom error type |
For example,
use errors.New
for an error with a static string.
Export this error as a variable to support matching it with errors.Is
if the caller needs to match and handle this error.
No error matching | Error matching |
---|---|
// package foo
func Open() error {
return errors.New("could not open")
}
// package bar
if err := foo.Open(); err != nil {
// Can't handle the error.
panic("unknown error")
} |
// package foo
var ErrCouldNotOpen = errors.New("could not open")
func Open() error {
return ErrCouldNotOpen
}
// package bar
if err := foo.Open(); err != nil {
if errors.Is(err, foo.ErrCouldNotOpen) {
// handle the error
} else {
panic("unknown error")
}
} |
For an error with a dynamic string,
use fmt.Errorf
if the caller does not need to match it,
and a custom error
if the caller does need to match it.
No error matching | Error matching |
---|---|
// package foo
func Open(file string) error {
return fmt.Errorf("file %q not found", file)
}
// package bar
if err := foo.Open("testfile.txt"); err != nil {
// Can't handle the error.
panic("unknown error")
} |
// package foo
type NotFoundError struct {
File string
}
func (e *NotFoundError) Error() string {
return fmt.Sprintf("file %q not found", e.File)
}
func Open(file string) error {
return &NotFoundError{File: file}
}
// package bar
if err := foo.Open("testfile.txt"); err != nil {
var notFound *NotFoundError
if errors.As(err, ¬Found) {
// handle the error
} else {
panic("unknown error")
}
} |
Note that if you export error variables or types from a package, they will become part of the public API of the package.