Lab 08: Error Handling

Objective

Master Go's error handling philosophy: errors as values, custom error types, errors.Is/As, wrapping, sentinel errors, and panic/recover.

Time

30 minutes

Prerequisites

  • Lab 05 (Interfaces)

Tools

  • Docker image: zchencow/innozverse-go:latest


Lab Instructions

Step 1: The error Interface

docker run --rm zchencow/innozverse-go:latest go run - << 'EOF'
package main

import (
    "errors"
    "fmt"
)

// errors.New — simple error
var ErrDivByZero = errors.New("division by zero")

// fmt.Errorf — formatted error
func safeDivide(a, b float64) (float64, error) {
    if b == 0 { return 0, ErrDivByZero }
    return a / b, nil
}

func parseAge(s string) (int, error) {
    var age int
    if _, err := fmt.Sscanf(s, "%d", &age); err != nil {
        return 0, fmt.Errorf("parseAge(%q): %w", s, err) // %w wraps the error
    }
    if age < 0 || age > 150 {
        return 0, fmt.Errorf("parseAge(%q): age %d out of range [0, 150]", s, age)
    }
    return age, nil
}

func main() {
    // Always check errors
    result, err := safeDivide(10, 3)
    if err != nil {
        fmt.Println("Error:", err)
    } else {
        fmt.Printf("10/3 = %.4f\n", result)
    }

    _, err = safeDivide(5, 0)

    // errors.Is — checks if error IS (or wraps) a specific error
    fmt.Println("Is ErrDivByZero:", errors.Is(err, ErrDivByZero))

    // Error wrapping chain
    age, err := parseAge("25")
    fmt.Printf("Age: %d, err: %v\n", age, err)

    _, err = parseAge("abc")
    fmt.Println("Error:", err)

    _, err = parseAge("200")
    fmt.Println("Error:", err)
}
EOF

💡 %w in fmt.Errorf wraps an error, maintaining the error chain. errors.Is(err, target) walks the entire chain — if any error in the chain matches target, it returns true. Without %w (using %v or %s instead), the original error is lost and errors.Is won't find it.

📸 Verified Output:


Step 2: Custom Error Types

💡 errors.As(err, &target) is the typed version of errors.Is. It finds the first error in the chain that is assignable to target and sets target to it. Use it to extract typed error details. Go 1.20+ supports errors.Join and returning multiple errors from a single call.

📸 Verified Output:


Steps 3–8: Panic/Recover, Sentinel Errors, Result Pattern, Error Middleware, Retry, Capstone

📸 Verified Output:


Summary

Pattern
When to use

errors.New("msg")

Simple sentinel error

fmt.Errorf("...: %w", err)

Wrap with context

Custom struct error

Typed errors with extra fields

errors.Is(err, target)

Check if error matches sentinel

errors.As(err, &target)

Extract typed error

panic/recover

Truly unexpected, unrecoverable state

Retry with backoff

Transient failures (network, I/O)

Further Reading

Last updated