Chapter 2: Error Handling Patterns
Error Handling Patterns
Section titled “Error Handling Patterns”Go’s explicit error handling is a deliberate design choice. Rather than exceptions that can bubble up unexpectedly, Go makes error handling visible and mandatory.
Error handling in Go is one of its most controversial features. Coming from languages with try/catch, the explicit if err != nil checks feel verbose and repetitive. But this verbosity serves a purpose: it makes error handling visible, predictable, and impossible to ignore.
Go’s philosophy is that errors are expected outcomes, not exceptional conditions. A file might not exist. A network request might fail. A user might enter invalid input. These aren’t exceptions - they’re normal program flow. Go treats them as such, forcing you to explicitly decide how to handle each error.
This chapter covers error fundamentals, custom error types, error wrapping and unwrapping, sentinel errors, and practical patterns for real-world error handling. You’ll learn not just the mechanics, but the philosophy and best practices that make Go error handling effective.
The Error Interface
Section titled “The Error Interface”Understanding Errors as Values
Section titled “Understanding Errors as Values”In Go, errors are just values that implement the error interface. This interface is remarkably simple - just one method that returns a string:
type error interface { Error() string}This simplicity is powerful. Any type can be an error by implementing Error(). Errors are returned like any other value. They can be stored, passed around, and inspected. There’s no special exception handling mechanism - just regular control flow.
Why this matters: Errors as values means you handle them where they occur, with full context. No catching exceptions three layers up the call stack and trying to figure out what went wrong. Error handling is local, explicit, and predictable.
The error return convention: Functions that can fail return (result, error). By convention, error is the last return value. nil error means success. Non-nil error means failure (and other return values should be ignored).
Example
Section titled “Example”Custom Error Types
Section titled “Custom Error Types”When and Why
Section titled “When and Why”Create custom error types when you need to include additional context beyond a simple error message. Custom errors can carry structured data - field names, error codes, retry information, HTTP status codes - that callers can programmatically inspect and act on.
The standard errors.New("message") is fine for simple cases, but real applications need more. A custom ValidationError can specify which field failed validation. A custom HTTPError can include the status code. A custom RetryableError can signal that an operation should be retried.
When to use custom errors:
- When callers need to make decisions based on error details
- When errors need structured context (which field, what value, why it failed)
- When different error types require different handling strategies
- When you want type-safe error inspection without string matching
How they work: Define a struct, implement the Error() string method, and you have a custom error type. Callers can use type assertions (err.(*ValidationError)) or errors.As() to extract the custom type and access its fields.
Implementation
Section titled “Implementation”Create custom error types when you need to include additional context:
Sentinel Errors
Section titled “Sentinel Errors”Sentinel errors are predefined error values for specific conditions:
Error Wrapping (Go 1.13+)
Section titled “Error Wrapping (Go 1.13+)”Wrap errors to add context while preserving the original error:
errors.Is and errors.As
Section titled “errors.Is and errors.As”Use errors.Is to check for specific errors and errors.As to extract typed errors:
Error Handling Patterns
Section titled “Error Handling Patterns”Handle Once
Section titled “Handle Once”Handle errors at one place, don’t log and return:
Fail Fast
Section titled “Fail Fast”Check errors immediately, don’t defer error handling:
Key Takeaways
Section titled “Key Takeaways”- Errors are values - the
errorinterface is justError() string - Custom errors - create types when you need additional context
- Sentinel errors - predefined errors for known conditions
- Wrap with %w - use
fmt.Errorf("context: %w", err)to add context - errors.Is/As - check error chains without unwrapping manually
- Handle once - either return an error or handle it, not both
Exercise
Section titled “Exercise”Error Chain Parser
Create a function that processes a file path. It should wrap errors at each level (validate path, check permissions, read file). Use errors.Is and errors.As to identify specific errors in the chain.
Next up: Chapter 3: Pointers & Memory