Go by Example

Custom Errors

Implementing the error interface with a struct to carry structured context callers can extract.

A custom error is a type that implements Error() string. Use a struct when callers need structured details such as an operation, resource, or status code.

Return the custom type as an error. Callers use errors.As to recover its fields, and Unwrap exposes an underlying cause.

package main
 
import (
    "errors"
    "fmt"
)
 
var ErrUnavailable = errors.New("service unavailable")
 
type RequestError struct {
    Method string
    URL    string
    Cause  error
}
 
func (e *RequestError) Error() string {
    return fmt.Sprintf("%s %s: %v", e.Method, e.URL, e.Cause)
}
 
func (e *RequestError) Unwrap() error {
    return e.Cause
}
 
func fetch(url string) error {
    return &RequestError{Method: "GET", URL: url, Cause: ErrUnavailable}
}
 
func main() {
    err := fetch("/users/99")
    var requestErr *RequestError
    if errors.As(err, &requestErr) {
        fmt.Println(requestErr.Method, requestErr.URL)
    }
    fmt.Println(errors.Is(err, ErrUnavailable))
}

In production

An error return keeps a function's contract decoupled from one implementation, while callers can still inspect details with errors.As. Implement Unwrap() error when the custom error carries a cause. Treat error text as human-readable output, not a stable machine-readable format.

Enjoyed this? Get more software craft delivered to your inbox.