Error Types
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.Isorerrors.Asfunctions 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.Errorfor 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 |
|---|---|
| ```go // 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") } ``` | ```go // 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 |
|---|---|
| ```go // 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") } ``` | ```go // 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.