Skip to content

Errors & Generics

Most languages handle failures through exceptions — a special control-flow mechanism that unwinds the call stack until a catch block intercepts the problem. Go takes a radically different approach: errors are ordinary values.

A function that can fail returns two values: the result and an error. The caller receives both and decides what to do. There is no hidden control flow, no stack unwinding, no unchecked exceptions. Every error-producing callsite is visible in the code, which makes programs easier to audit and reason about.

The canonical pattern you will see everywhere in Go:

result, err := doSomething()
if err != nil {
// handle the failure — return, log, wrap, or recover
}
// err is nil: safe to use result

nil means success. A non-nil error means something went wrong and result should not be trusted.

Go’s error type is defined as a single-method interface in the standard library:

type error interface {
Error() string
}

Any type that implements Error() string satisfies the error interface. This means you can pass simple string-based errors, richly structured errors, and everything in between — all through the same interface. nil is a valid error value and signals the absence of any error.

LessonTopic
This pagePhilosophy overview and first runnable example
Error Valueserrors.New, fmt.Errorf, sentinel errors, errors.Is
Wrapping & Custom Errors%w, errors.As, custom error types
Panic & RecoverWhen to panic, recover, deferring cleanup
GenericsType parameters, constraints, ~T, union types

Here is a minimal function that returns (string, error), demonstrating the fundamental shape of Go error handling:

package main
import (
"errors"
"fmt"
)
func greet(name string) (string, error) {
if name == "" {
return "", errors.New("name must not be empty")
}
return fmt.Sprintf("Hello, %s!", name), nil
}
func main() {
msg, err := greet("Gopher")
if err != nil {
fmt.Println("error:", err)
return
}
fmt.Println(msg) // Hello, Gopher!
_, err = greet("")
if err != nil {
fmt.Println("error:", err) // error: name must not be empty
}
}

errors.New creates a simple error value from a static string. The function returns nil as the error on the happy path and a non-nil error on the failure path. The caller checks if err != nil immediately after the call — this is the idiom you will write hundreds of times in Go.

In Go, what does a function signal by returning a non-nil error?
What is the definition of the built-in error interface?
Which package provides errors.New and errors.Is?
Generics were added to Go in version: