🎓 A1Learner
🔍
0 of 34 lessons completed

Go Panic and Recover

A panic is a failure that stops the normal flow of a function and unwinds the chain of calls. Recover is a built-in call that stops a panic and returns the value given to panic.

A deferred recover catches a panic

The deferred literal calls recover. It prints the value that panic was given.

package main

import "fmt"

func run() {
	defer func() {
		value := recover()
		fmt.Println("Recovered:", value)
	}()
	panic("something broke")
}

func main() {
	run()
}

The program continues after a recover

task panics and its deferred literal recovers the value. Main prints its next line after task returns.

package main

import "fmt"

func task() {
	defer func() {
		value := recover()
		fmt.Println("Recovered:", value)
	}()
	panic("task failed")
}

func main() {
	task()
	fmt.Println("Program continues")
}

A panic in a helper is recovered in the caller

The deferred literal lives in caller, one frame above fail. The print line after the fail call never runs.

package main

import "fmt"

func fail() {
	panic("helper failed")
}

func caller() {
	defer func() {
		value := recover()
		fmt.Println("Recovered:", value)
	}()
	fail()
	fmt.Println("After helper")
}

func main() {
	caller()
}

recover returns nil when nothing panicked

Both functions defer the same guard print. The calm one sees nil and the risky one sees a panic value.

package main

import "fmt"

func calm() {
	defer func() {
		value := recover()
		fmt.Println("Recovered value is nil:", value == nil)
	}()
	fmt.Println("calm work")
}

func risky() {
	defer func() {
		value := recover()
		fmt.Println("Recovered value is nil:", value == nil)
	}()
	panic("risk failed")
}

func main() {
	calm()
	risky()
}

Example

package main

import "fmt"

func divide(a int, b int) (result int) {
	defer func() {
		value := recover()
		if value != nil {
			fmt.Println("Recovered:", value)
			result = 0
		}
	}()
	if b == 0 {
		panic("division by zero")
	}
	return a / b
}

func main() {
	fmt.Println(divide(10, 0))
	fmt.Println(divide(10, 2))
}

Key points

  • Use panic with a value to start a panic on purpose.
  • A panic climbs back through the calling functions as it unwinds.
  • An unwinding panic runs the deferred calls of each function it leaves.
  • Use recover inside a deferred function to stop a panic.
  • recover returns the value that was passed to panic.
  • recover returns nil when no panic is happening.
  • After a recover, the enclosing function returns and the program continues.

In this example, divide panics when the second number is zero. The deferred literal recovers the panic value and prints it. It sets the named result to 0, so main prints 0 for that call. The second call divides 10 by 2 and prints 5.

Note

A panic that no function recovers ends the program.

Try it Yourself

Edit the code, then press Run.

Running is simulated · Go Playground

Exercise

Where must recover be called to stop a panic?