"Should I panic here?" Only if it's really broken. In Go, panics are the fire alarm β not your main error strategy.
π¨ 1. Soβ¦ What Is panic()
?
panic()
immediately stops the normal execution flow.
panic("something went terribly wrong")
Once a panic happens:
- The function exits
- All its deferred functions are run
- The panic bubbles up to the caller
- Eventually, the app crashes and prints a stack trace
func main() {
panic("π£ Boom!")
fmt.Println("you'll never see this")
}
β³ 2. But What About Cleanup? β defer
to the Rescue
Go runs defer
statements even during a panic.
func main() {
defer fmt.Println("cleaning up...")
panic("something exploded")
}
Output:
cleaning up...
panic: something exploded
So yes, you can still close files, release locks, etc. even in chaos.
π§― 3. Recovering from a Panic β But Only With recover()
Use recover()
inside a deferred function to catch the panic and prevent a full crash.
func safeCall() {
defer func() {
if r := recover(); r != nil {
fmt.Println("Recovered:", r)
}
}()
panic("unexpected error")
}
Output:
Recovered: unexpected error
β The program continues β The error is not automatically logged, handled, or understood β you must do something with it.
π¨βπ§ 4. When Should You Use Panic?
β
Use panic()
for programmer bugs, not user mistakes.
Examples:
- Corrupted internal state
- Impossible logic condition
- Invalid memory reference
- Unrecoverable system failure
β Don't panic when:
- A file doesn't exist
- A user sends bad input
- A network call times out
For those: use error
, not panic
.
π 5. Common Use: Crash Early in Package Init
var config = loadConfig()
func loadConfig() Config {
data, err := os.ReadFile("config.json")
if err != nil {
panic("could not read config: " + err.Error())
}
// parse config...
}
In some cases, it's okay to crash during startup β better to fail loud than run broken.
π§ Advanced Pattern: Wrapping Panics into Errors
You can build functions that convert panics into errors:
func safeDivide(a, b int) (result int, err error) {
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("panic caught: %v", r)
}
}()
if b == 0 {
panic("division by zero")
}
return a / b, nil
}
res, err := safeDivide(10, 0)
fmt.Println("err:", err) // err: panic caught: division by zero
Useful in libraries and tools where robustness matters more than purity.
βοΈ 6. Panic vs Error Quick Table
| Case | Use `error` | Use `panic()` |
| ----------------------------------- | ----------- | --------------------------- |
| Invalid user input | β
| β |
| Missing config file | β
| Maybe (if fatal) |
| Internal bug (should never happen) | β | β
|
| Network failure | β
| β |
| Corrupted memory / data | β | β
|
| Logic error like index out of range | β | β
(Go panics automatically)|
π§Ή 7. Graceful Shutdowns Using defer
+ recover
func main() {
defer func() {
if r := recover(); r != nil {
log.Println("Something broke:", r)
// Maybe: restart app, alert system, clean exit
}
}()
runApp()
}
You'll often see this pattern in:
- HTTP servers
- Goroutines
- Workers/daemons
It's defensive programming β you stay alive even when something explodes.
π« But Don't Overuse It
- Panic is not Go's exception system.
- It should never replace
error
handling. - If you panic too much, you lose the clarity Go is known for.
Go's simplicity depends on manual, visible control flow β not hidden stack jumping.
β Summary
Go gives you panic/recover β but trusts you to use it wisely. It's the emergency brake, not the steering wheel.
In real-world Go:
- You return errors for expected failures.
- You panic when something's broken at the core.
Keep your crashes intentional, controlled, and rare. π