Which runtime failures can't be recovered with recover?
Question 311HardGo 1.22 to 1.25
recover handles panics, both user panics and runtime-error panics (nil dereference, index out of range, nil map write, division by zero, failed type assertion, closing a closed channel). Some failures are fatal errors that bypass defer and recover and kill the process immediately:
fatal error: concurrent map writes/concurrent map read and map writefatal error: all goroutines are asleep - deadlock!- Stack overflow (
goroutine stack exceeds 1000000000-byte limit) from runaway recursion - Out of memory (
runtime: out of memory) - Unlocking an unlocked
sync.Mutex(fatal error: sync: unlock of unlocked mutex)
func main() {
defer func() { fmt.Println("never printed:", recover()) }()
m := map[int]int{}
for i := range 100 {
go func() { m[i] = i }() // usually: fatal error: concurrent map writes
}
time.Sleep(time.Second)
}
The runtime's map race check is best-effort (it still exists with the Swiss-table maps of Go 1.24), so a racy program may also just corrupt data silently. Likewise, os.Exit and log.Fatal skip deferred functions entirely. What the interviewer is checking: that you don't treat recover as a safety net for data races. Prevent those with sync.Mutex, sync.Map, or channels, and find them with go test -race.
More on Error Handling & panics
- Q309How do you convert a panic into an error returned from a function?
- Q310What happens with
panic(nil)and with re-panicking? How did Go 1.21 change this? - Q312Compare
panic,os.Exit,log.Fatalandruntime.Goexitwith respect to deferred functions. - Q313How do you propagate errors from multiple goroutines? Explain
errgroup. - Q314How do you collect all errors from concurrent workers rather than just the first?
- Q315Go errors don't carry stack traces. How do you get stack information when you need it?