Go

What are the rules for recover? Why doesn't defer recover() stop a panic?

Question 18HardGo 1.22 to 1.25

recover stops a panic only when it is called directly by a deferred function while the goroutine is panicking. In every other situation it returns nil and does nothing.

func a() {
	defer recover() // does NOT recover: recover is the deferred function itself,
	panic("x")      // it is not called BY a deferred function
}

func helper() { recover() }
func b() {
	defer func() { helper() }() // does NOT recover: called one level too deep
	panic("x")
}

func c() {
	defer func() {
		if r := recover(); r != nil { // works
			log.Println("recovered:", r)
		}
	}()
	panic("x")
}

Other important points:

  • A panic only unwinds its own goroutine. A panic in a goroutine that nobody recovers crashes the whole program. A recover in main can't catch it, so every goroutine you start needs its own recovery if you want protection.
  • After recovering, the function returns normally. Set named results if the caller needs to know something went wrong.
  • Since Go 1.21, panic(nil) becomes a *runtime.PanicNilError, so recover() never returns nil during a real panic.
  • Some fatal errors can't be recovered at all: concurrent map writes, running out of memory, deadlock, and stack overflow.

More on Language Fundamentals & Types

All 39 Language Fundamentals & Types questions