# Why panic and recover Matter
In Go, error is for expected failures, while panicis for programmer bugs and impossible states.
recover lets you stop a panic from crashing the process, usually at boundaries like HTTP middleware, worker loops, and goroutine entry points.
Real-World Analogyclick to expand
Think of panic as a circuit breaker tripping: execution stops immediately. recover is a control panel reset done by an operator in a safe boundary area.
Inside business logic, you return errors. At boundaries, you recover to keep the whole service alive.
# panic Basics
A panic unwinds the current goroutine stack, running deferred functions in LIFO order.
func divide(a, b int) int {
if b == 0 {
panic("cannot divide by zero")
}
return a / b
}# recover Basics
recover() only works when called directly inside a deferred function.
func safeRun(fn func()) (err error) {
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("panic caught: %v", r)
}
}()
fn()
return nil
}# Step-by-Step Rules
Use panic for invariants, not expected errorsValidation, I/O, and network failures should return error values.
recover must be in defer on same goroutineA panic cannot be recovered from another goroutine.
panic still runs deferred cleanupThis is why defer is critical around locks/files even in code that might panic.
# panic vs error in Practice
// Expected failure -> error return
func readFile(path string) ([]byte, error) { ... }
// Impossible state -> panic
func mustBePositive(n int) {
if n <= 0 {
panic(fmt.Sprintf("BUG: expected positive, got %d", n))
}
}
// Boundary recovery middleware
defer func() {
if r := recover(); r != nil {
http.Error(w, "Internal Server Error", 500)
}
}()# Real-World Example: Must Helper
Must is useful in initialization code where failure should stop startup.
package main
import (
"fmt"
"strconv"
)
func Must[T any](v T, err error) T {
if err != nil {
panic(err)
}
return v
}
func main() {
n := Must(strconv.Atoi("42"))
fmt.Println(n)
n = Must(strconv.Atoi("not a number"))
fmt.Println(n)
}⚡ Key Takeaways
- Use
errorfor expected failures andpanicfor invariant violations recover()only works in deferred functions on the same goroutine- Panics unwind stack and run deferred calls in LIFO order
- Recover at boundaries (HTTP middleware, worker loops), not in every function
Musthelpers are useful for startup-time unrecoverable failures