Go Error Handling and Best Practices

Go provides a simple but powerful error handling mechanism.

Basics of Error Handling in Go

Errors in Go are represented using the built-in error interface:

type error interface {
    Error() string
}

A function that can fail typically returns an error as the last return value:

package main

import (
    "errors"
    "fmt"
)

func divide(a, b float64) (float64, error) {
    if b == 0 {
        return 0, errors.New("division by zero")
    }
    return a / b, nil
}

func main() {
    result, err := divide(10, 0)
    if err != nil {
        fmt.Println("Error:", err)
        return
    }
    fmt.Println("Result:", result)
}

Creating Custom Errors

You can define custom error types to provide more context:

type DivideError struct {
    A, B float64
    Msg   string
}

func (e *DivideError) Error() string {
    return fmt.Sprintf("cannot divide %f by %f: %s", e.A, e.B, e.Msg)
}

func divide(a, b float64) (float64, error) {
    if b == 0 {
        return 0, &DivideError{a, b, "division by zero"}
    }
    return a / b, nil
}

Using fmt.Errorf for Wrapping Errors

Go 1.13+ introduced error wrapping using fmt.Errorf:

import (
    "fmt"
    "errors"
)

func readConfig() error {
    return errors.New("failed to read config file")
}

func main() {
    err := readConfig()
    if err != nil {
        fmt.Println(fmt.Errorf("application startup error: %w", err))
    }
}

Using errors.Is and errors.As

To check if an error matches a specific type:

if errors.Is(err, os.ErrNotExist) {
    fmt.Println("File does not exist")
}

To extract error details:

var e *DivideError
if errors.As(err, &e) {
    fmt.Println("Custom error occurred:", e)
}

Best Practices for Error Handling in Go

  1. Return errors instead of panicking – Use panic only for truly exceptional situations.
  2. Use error wrapping – Provide context when propagating errors.
  3. Use sentinel errors sparingly – Predefined errors like io.EOF can be useful but should not be overused.
  4. Log errors properly – Use structured logging for better debugging.