Designing Errors in Go

Choose between sentinel errors and custom error types, decide when to panic instead of returning an error, and avoid the nil error interface trap.

Once you can create and wrap errors, the next questions are about design. What should your package return so callers can react? When is a crash better than an error? And why does a function that returns nil sometimes produce an error anyway? This section answers those three questions.

Sentinel Errors vs Custom Error Types

A sentinel is a shared variable like var ErrNotFound = errors.New("not found") that callers check with errors.Is. A custom error type is a struct with an Error method that carries details, unpacked with errors.As.

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

type ValidationError struct {
	Field, Reason string
}

Use a sentinel when callers only need to know which condition happened, and a type when they need data from it.

Panic vs Returning an Error

Return an error for anything that can fail in a correct program: input, files, network, databases. Panic only for bugs, impossible states and startup setup from constants, usually through Must helpers such as regexp.MustCompile.

Panics in goroutines kill the whole program:

An unrecovered panic in any goroutine stops every part of the program, not just the code that failed.

Why a nil Error Is Not nil

Returning a nil pointer of a custom error type through an error result makes err != nil true. Always return nil literally on success.

Summary

Good error design means callers can handle every case they care about with errors.Is or errors.As, panics are reserved for real bugs, and success always returns a true nil.

Sentinel Errors vs Custom Error Types in Go

When to expose a named error variable like ErrNotFound, when to define your own error struct, and how callers check each one.

Panic vs Returning an Error in Go

When a Go function should return an error and when it should panic, what a panic does to your program, and how to turn a panic into an error safely.

Why a nil Error Is Not nil in Go

Why returning a nil pointer of a custom error type makes err != nil true, how to spot the bug, and the simple rule that prevents it.