The error Interface
Go doesn't have exceptions. Instead, functions return an error value as the last return value. The caller checks if err != nil and handles the error explicitly. This makes error handling visible and forces you to think about failures.
import (
"errors"
"fmt"
"os"
)
func readConfig(path string) ([]byte, error) {
data, err := os.ReadFile(path)
if err != nil {
return nil, fmt.Errorf("reading config %s: %w", path, err)
}
return data, nil
}
func main() {
data, err := readConfig("config.json")
if err != nil {
fmt.Println("Error:", err)
return
}
fmt.Println("Config loaded:", len(data), "bytes")
}
_ unless you have a very good reason and document it.Error Wrapping with %w
fmt.Errorf with %w wraps errors, preserving the original cause while adding context. errors.Is and errors.As unwrap to check the original error:
var ErrNotFound = errors.New("not found")
func findUser(id int) (*User, error) {
// ... database query ...
return nil, fmt.Errorf("findUser(%d): %w", id, ErrNotFound)
}
_, err := findUser(42)
// errors.Is checks the error chain
if errors.Is(err, ErrNotFound) {
fmt.Println("User not found") // This matches!
}
// errors.As extracts a specific error type
var pathErr *os.PathError
if errors.As(err, &pathErr) {
fmt.Println("Path:", pathErr.Path)
}
Custom Error Types
type ValidationError struct {
Field string
Message string
}
func (e *ValidationError) Error() string {
return fmt.Sprintf("validation error on %s: %s", e.Field, e.Message)
}
func validateAge(age int) error {
if age < 0 || age > 150 {
return &ValidationError{Field: "age", Message: "must be 0-150"}
}
return nil
}
err := validateAge(-5)
var valErr *ValidationError
if errors.As(err, &valErr) {
fmt.Printf("Field: %s, Message: %s\n", valErr.Field, valErr.Message)
}
⚠️ Common Mistake: Comparing errors with ==
Use errors.Is(err, target) instead of err == target. errors.Is unwraps the error chain, so it works with wrapped errors. == only matches the exact error instance.
Practice Exercises
Medium Build a Mini Project
Combine concepts from this tutorial to build a small utility or tool.
Medium Debug Challenge
Introduce a bug in one of the code examples and practice finding and fixing it.
Hard Refactoring Exercise
Rewrite one example using a different approach and compare the tradeoffs.