Go - Error handling

Source: Implement error handling and logging in Go

Intro

In Go, a function that could fail should always return an additional value so that you can anticipate and manage a failure successfully.

Go has built-in functions like panic and recover to manage exceptions, or unexpected behavior, in your programs. But errors are known failures that your programs should be built to handle

employee, err := getInformation(1000)
if err != nil {
    // Something is wrong. Do something.
}

If the error is nil, that means success. If it’s not nil, that means failure. A non-nil error comes with an error message that you can either print or, preferably, log.

Error handling strategies

Propagate the error in a subroutine

Return the error to the caller without doing anything else:

func getInformation(id int) (*Employee, error) {
    employee, err := apiCallEmployee(1000)
    if err != nil {
        return nil, err // Simply return the error to the caller.
    }
    return employee, nil
}

If we want to include more information in the error message, we can use the fmt.Errorf function:

  if err != nil {
      return nil, fmt.Errorf("Got an error when getting the employee information: %v", err)
  }

Run retry logic

func getInformation(id int) (*Employee, error) {
    for tries := 0; tries < 3; tries++ {
        employee, err := apiCallEmployee(1000)
        if err == nil {
            return employee, nil
        }

        fmt.Println("Server is not responding, retrying ...")
        time.Sleep(time.Second * 2)
    }

    return nil, fmt.Errorf("server has failed to respond to get the employee information")
}

Create reusable errors

Use the errors.New() function to create errors and reuse them in several parts.

var ErrNotFound = errors.New("Employee not found!")

func getInformation(id int) (*Employee, error) {
    if id != 1001 {
        return nil, ErrNotFound
    }

    ...
}

Good practice: Err prefix for error variables.

The errors.Is() function allows you to compare the type of error.

if errors.Is(err, ErrNotFound) {
    fmt.Printf("NOT FOUND: %v\n", err)
} else {
    fmt.Print(employee)
}