error is a built-in one-method interface. Go treats errors as values, not exceptions.
Examples
A package-level value works as a comparable marker.
var ErrNotFound = errors.New("not found")
func find(id int) error {
if id != 1 {
return ErrNotFound
}
return nil
}
err := find(2)
fmt.Println(errors.Is(err, ErrNotFound))Output
true%w wraps the error instead of flattening it to text.
func readConfig(name string) error {
_, err := os.Open(name)
if err != nil {
return fmt.Errorf("read config: %w", err)
}
return nil
}
err := readConfig("missing.json")
fmt.Println(err)
fmt.Println(errors.Is(err, os.ErrNotExist))Output
read config: open missing.json: no such file or directory
trueWalks the wrap chain for a specific concrete type.
type ValidationError struct {
Field string
}
func (e *ValidationError) Error() string {
return fmt.Sprintf("invalid field: %s", e.Field)
}
func validate(age int) error {
if age < 0 {
return &ValidationError{Field: "age"}
}
return nil
}
err := validate(-1)
var verr *ValidationError
if errors.As(err, &verr) {
fmt.Println(verr.Field)
}Output
age| Function | Description |
|---|---|
New(text string) error var ErrNotFound = errors.New("not found") | Assign once to a package var, compare with Is. |
Is(err, target error) bool if errors.Is(err, io.EOF) { ... } | Matches through any amount of wrapping. |
As(err error, target any) bool errors.As(err, &verr) | Fills *target as a side effect. Check the bool first. |
Unwrap(err error) error inner := errors.Unwrap(err) | One level down. Is/As already do this for you. |
Join(errs ...error) error err := errors.Join(err1, err2) | Bundles errors; Is/As still match the originals. |
ErrUnsupported error errors.Is(err, errors.ErrUnsupported) | A ready-made sentinel, not a function. |