In Go, it’s idiomatic to communicate errors via an explicit, separate return value. This contrasts with the exceptions used in languages like Java, Python, and Ruby. Go’s approach makes it easy to see which functions return errors and to handle them using the same language constructs employed for other tasks.
By convention, errors are the last return value and have type error, a built-in interface.
func f(arg int) (int, error) { if arg == 42 { // errors.New constructs a basic error value // with the given error message return -1, errors.New("can't work with 42") } // A nil value in the error position indicates that // there was no error return arg + 3, nil}
You can wrap errors with higher-level errors to add context. The simplest way to do this is with the %w verb in fmt.Errorf:
func makeTea(arg int) error { if arg == 4 { // Wrapped errors create a logical chain (A wraps B, which wraps C, etc.) // that can be queried with functions like errors.Is and errors.AsType return fmt.Errorf("making tea: %w", ErrPower) } return nil}
Wrapped errors create a logical chain that can be queried with functions like errors.Is and errors.AsType.
errors.Is checks that a given error (or any error in its chain) matches a specific error value. This is especially useful with wrapped or nested errors:
for i := range 5 { if err := makeTea(i); err != nil { if errors.Is(err, ErrOutOfTea) { fmt.Println("We should buy new tea!") } else if errors.Is(err, ErrPower) { fmt.Println("Now it is dark.") } else { fmt.Printf("unknown error: %s\n", err) } continue } fmt.Println("Tea is ready!")}