Stage 1 · Code
Methods, Interfaces & Errors
Error Values
Create, wrap, inspect, and handle errors the idiomatic Go way — explicit, composable, and never hidden.
Creating Errors
The error interface has one method: Error() string. You create simple error values with errors.New for static messages and fmt.Errorf for dynamic messages with context.
Wrapping with %w
When you add context to an error before returning it, wrap it with %w so callers can still inspect the original error. Without %w, the original error is buried in a string and lost.
errors.Is and errors.As
errors.Is(err, target) checks whether any error in the chain is equal to target. errors.As(err, &target) checks whether any error in the chain has a specific type and stores it in target.
Custom Error Types
Implement the error interface on your own type when callers need structured information beyond a string message — for example, an HTTP status code, a retry delay, or a field name that failed validation.
Handling Errors at Boundaries
Most functions should propagate errors upward with added context. Only the top of the call stack — main, a request handler, a background worker — should ultimately decide what to do: log, retry, or terminate.
- Low-level functions: return the error with
fmt.Errorf("context: %w", err). - Mid-level functions: wrap and propagate — add their own context.
- Top-level boundary (main, HTTP handler): log the full chain and decide on the response.
- Never ignore an error by assigning to
_without a documented reason. - Never
log.Fataldeep inside a library — that decision belongs to the caller.
A common mistake is both logging an error and returning it. The caller then logs it again, producing duplicate noise. Pick one: either handle the error (log it, respond to it) or propagate it. If you propagate, add context with %w and let the top handle it.
Mark this lesson complete to store local progress and unlock a cleaner resume path the next time you visit.