Skip to main content

Error Handling

Status

This note is complete, reviewed, and considered stable.

Error handling in Go (Golang) is a fundamental part of writing robust and reliable programs. Unlike many other languages that use exceptions, Go uses a simple, explicit, and lightweight approach based on returning error values.

The error Type​

In Go, errors are represented by the built-in error type, which is an interface:

type error interface {
Error() string
}

Any type that implements the Error() method with the signature Error() string satisfies the error interface.

Returning and Checking Errors​

Functions that can fail typically return an error as the last return value. The caller is responsible for checking this value:

func readFile(filename string) ([]byte, error) {
// ...
}

data, err := readFile("data.txt")
if err != nil {
// handle the error
log.Fatal(err)
}
// use data

Creating Errors​

We can create errors using the errors.New function or fmt.Errorf for formatted errors:

import (
"errors"
"fmt"
)

err1 := errors.New("something went wrong")
err2 := fmt.Errorf("failed to open file: %s", filename)

Custom Error Types​

For more context, we can define custom error types:

type MyError struct {
Code int
Message string
}

func (e *MyError) Error() string {
return fmt.Sprintf("Error %d: %s", e.Code, e.Message)
}

// Usage
return &MyError{Code: 404, Message: "Not Found"}

Error Wrapping and Unwrapping​

Go 1.13 introduced error wrapping with %w in fmt.Errorf and the errors.Is and errors.As functions for error inspection:

wrappedErr := fmt.Errorf("operation failed: %w", err)
if errors.Is(wrappedErr, err) {
// err is in the error chain
}

Sentinel Errors​

Sometimes, we define package-level variables for common errors (sentinel errors):

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

func findItem(id int) error {
if id == 0 {
return ErrNotFound
}
return nil
}

if err == ErrNotFound {
// handle not found
}

Panic and Recover​

Go does have a panic/recover mechanism, but it is reserved for truly exceptional situations (e.g., unrecoverable errors):

func mayPanic() {
defer func() {
if r := recover(); r != nil {
fmt.Println("Recovered from panic:", r)
}
}()
panic("something bad happened")
}