Panic & Recover
5/4/24About 4 min
Panic & Recover | Exception Handling
In Golang, panic and recover are two keywords used for exception handling. panic is used to raise an exception, while recover is used to catch exceptions.
Tips
| Function | Description |
|---|---|
panic(interface{}) | Raises an exception, stops the execution of the current Goroutine, and recursively executes the defer methods within the current Goroutine. |
recover() interface{} | Catches an exception, returns the exception information, and is used to handle exceptions, preventing program crashes due to exceptions. It can only be called within a defer block. |
Panic Data Structure
The data structure for panic is defined in the runtime._panic struct:
// _panic stores information about an active panic.
//
// _panic values can only exist on the stack.
//
// argp and link fields are stack pointers, but they do not need special handling during stack growth:
// because they are pointer types and _panic values only exist on the stack, regular stack pointer adjustments handle them.
type _panic struct {
argp unsafe.Pointer // Points to the arguments of deferred calls that ran during the panic; cannot move - known to liblink
arg any // The argument to panic
link *_panic // Points to the link of the previous panic
// startPC and startSP track where _panic.start was called.
startPC uintptr
startSP unsafe.Pointer
// We're running the current stack frame of a deferred call.
sp unsafe.Pointer
lr uintptr
fp unsafe.Pointer
// If the function returned by _panic.next() recovers the panic, retpc stores the PC to jump back to.
retpc uintptr
// Additional state for handling inlined defers.
deferBitsPtr *uint8
slotsPtr unsafe.Pointer
recovered bool // Whether this panic has been recovered
goexit bool
deferreturn bool
}- The
_panicstruct is used to store information about an active panic. _panicvalues can only exist on the stack and are not allocated on the heap.- The
argpandlinkfields are stack pointers, but they do not require special handling during stack growth. Because they are pointer types and_panicvalues only exist on the stack, regular stack pointer adjustments handle them. startPCandstartSPtrack where_panic.startwas called.sppoints to the current stack frame of a deferred call.retpcstores the PC to jump back to if the function returned by_panic.next()recovers the panic.recoveredindicates whether this panic has been recovered.
Scope of panic
paniconly triggers thedeferwithin the current Goroutine.
package main
import (
"fmt"
"time"
)
func main() {
for i := range 4 {
go testPanic(i)
}
time.Sleep(time.Second)
}
func testPanic(i int) {
defer func() {
if r := recover(); r != nil {
fmt.Println("recover panic: ", i)
}
}()
if i == 1 {
panic(fmt.Sprintf("panic %d", i))
}
fmt.Println("test panic: ", i)
}
// Result
// test panic: 0
// recover panic: 1
// test panic: 3
// test panic: 2recoveris used to capture exceptions, return exception information, and handle exceptions to prevent program crashes caused by exceptions. It is only valid when called indefer, and calling elsewhere will only returnnil.paniccan also be called indefer
package main
import (
"fmt"
"time"
)
func main() {
for i := range 4 {
go testPanic(i)
}
time.Sleep(time.Second)
}
func testPanic(i int) {
defer func() {
if r := recover(); r != nil {
fmt.Println("recover panic: ", i)
panic("panic in defer")
}
}()
if i == 1 {
panic(fmt.Sprintf("panic %d", i))
}
fmt.Println("test panic: ", i)
}
// Result
// test panic: 3
// test panic: 0
// test panic: 2
// recover panic: 1
// panic: panic 1 [recovered]
// panic: panic in deferExecution Flow of Panic

Execution Flow
- The compiler translates
panicinto a call to thegopanicfunction in theruntimepackage. - The
deferchain for the Goroutine is executed in reverse order. - If there is no
recoverin thedefer, the code within thedeferis executed. - If there is a
recoverin thedefer, theruntime.gorecoverfunction is called. It sets therecoveredfield in thepanictotrue, retrieves the program counter (pc) and stack pointer (sp) fromruntime._defer, executesruntime.recoveryto recover the program, and finally callsruntime.deferprocwith a return value of1to indicate successful recovery. - The
deferreturnfield of thepanicis set totrue, indicating that thedeferhas completed execution. - If none of the
deferblocks contain arecover(), the program terminates by executingruntime.fatalpanic.
Example
package main
import (
"fmt"
)
func main() {
defer func() {
fmt.Println("func 1")
}()
defer func() {
fmt.Println("func 2")
}()
defer func() {
fmt.Println("func 3")
if r := recover(); r != nil {
fmt.Println("recover")
}
}()
panic("panic")
}
// Output:
// func 3
// recover
// func 2
// func 1Exception Handling
Difficult-to-Catch Exception Types
The following exceptions cannot be caught using recover:
- Out of Memory: When pre-allocated memory space is too large and results in an out-of-memory condition, the error message will be
runtime: out of memory. - Concurrent Map Read and Write: Attempting concurrent read and write operations on a map will result in the error message
concurrent map read and map write. - Stack Exhaustion: When the stack memory is exhausted, the error message will be
runtime: goroutine stack exceeds 1000000000-byte limit. - Goroutine Running on NULL Machine: Running a Goroutine on a NULL machine will result in the error message
runtime: goroutine running on NULL machine. - All Goroutines Asleep (Deadlock): When all Goroutines are asleep and the program is deadlocked, the error message will be
all goroutines are asleep - deadlock!.
Catchable Exceptions
The following exceptions can be caught using recover:
- Array Index Out of Range: When an array index is out of range, the error message will be
panic: runtime error: index out of range. - Nil Pointer Dereference: Attempting to dereference a nil pointer will result in the error message
panic: runtime error: invalid memory address or nil pointer dereference. - Type Assertion Failure: When a type assertion fails, the error message will be
panic: interface conversion: interface {} is nil, not int. - Division by Zero: Attempting to divide by zero will result in the error message
panic: runtime error: integer divide by zero. - Calling an Undefined Method: Calling a method that does not exist will result in the error message
panic: runtime error: invalid memory address or nil pointer dereference.