Skip to content

Intermediate · Pointers, interfaces and errors · topic 20 of 46 · 25 min

panic and recover: only for the truly unexpected

Explanation

A panic stops the normal flow of a goroutine, runs its deferred calls, and, if nothing recovers, crashes the program with a stack trace. recover, called inside a deferred function, can stop a panic. Use errors for expected problems (bad input, missing file) and panic only for bugs and impossible situations.

What a panic is

A panic is Go's emergency stop. It happens when the program hits something it cannot continue from, like reading index 10 of a 3-item slice, following a nil pointer, or writing to a nil map. You can also call panic yourself. When a panic starts, the current function stops, deferred calls run (in reverse order) up through the calling functions, and then the program exits with a message and a stack trace (the list of function calls that led there).

go
func main() {
	defer fmt.Println("deferred: still runs")
	s := []int{1, 2, 3}
	i := 5
	fmt.Println(s[i])
	fmt.Println("never printed")
}
// deferred: still runs
// panic: runtime error: index out of range [5] with length 3
// goroutine 1 [running]: ...
The deferred call runs before the crash report.

Errors versus panics

Use an error whenA panic is acceptable when
The user typed bad inputThere is a programming bug: a case that "can never happen" happened
A file or network call failsThe program cannot start at all (a required template or regex in the code is invalid)
A record is not foundA Must-style helper is used with fixed values you control
Anything a caller might reasonably handleContinuing would corrupt data

The standard library follows this. regexp.MustCompile panics if the pattern is invalid, because the pattern is usually a fixed string in your code and a bad one is a bug you want to see immediately at start-up. regexp.Compile returns an error, for patterns that come from users.

go
var pinRe = regexp.MustCompile(`^[1-9][0-9]{5}$`) // fixed pattern: Must is fine

func userPattern(p string) (*regexp.Regexp, error) {
	return regexp.Compile(p) // user input: return the error
}
Must functions are for values written in your code, usually at package level.

recover

recover() stops a panic, but only when called directly inside a deferred function in the goroutine that is panicking. It returns the value passed to panic (or nil if there is no panic). After recovering, the function that deferred it returns normally to its caller.

go
func safeDivide(a, b int) (q int, err error) {
	defer func() {
		if r := recover(); r != nil {
			err = fmt.Errorf("recovered: %v", r)
		}
	}()
	return a / b, nil
}

func main() {
	fmt.Println(safeDivide(10, 2)) // 5 <nil>
	fmt.Println(safeDivide(1, 0))  // 0 recovered: runtime error: integer divide by zero
}
The deferred closure turns the panic into an error through the named result err.

Where recover is used in real code

  • HTTP servers: net/http already recovers a panic in a handler, logs it and closes that connection, so one bad request does not kill the server. Many teams also add their own recovery middleware to return a clean 500 response and log details (module 8).
  • Worker pools: a long-running worker recovers so one bad job is logged instead of stopping all processing.
  • Libraries that use panic internally for deep recursion (like parsers) and convert it to an error at their public boundary.
Careful: A panic in a goroutine that does not recover crashes the WHOLE program, not just that goroutine. A recover in main does not help: recover only works in the same goroutine. Each goroutine that might panic needs its own deferred recover.
Careful: Do not use panic and recover as try/catch for normal errors. It hides the error flow, is slower, and surprises other Go developers. Return errors.
Tip: Exit codes: an unrecovered panic exits with status 2. os.Exit(1) is the usual way for a CLI to report failure (module 9). Neither runs remaining deferred calls in other goroutines.

Need help?

Stuck on a word or an idea? Search this course, or every course, and jump straight to the explanation or a worked question.

Try:

Watch the lesson

panic and recover: only for the truly unexpected · 1/7

panic and recover

A panic is Go's emergency stop for things that should never happen.

Prefer a video by a teacher? Find free videos on “golang panic recover defer when to use” on YouTube ↗

In real life: A job worker that survives a bad record

A nightly job processes lakhs of GST invoices. One corrupted record triggers a nil pointer bug. Without recover, the whole job crashes at 2 a.m. and nothing is processed. With a recover around each job, that one invoice is logged for a developer to look at, and the rest are processed.

go
package main

import "fmt"

type Invoice struct {
	ID    string
	Buyer *string
}

func process(inv Invoice) {
	fmt.Println(inv.ID, "buyer:", *inv.Buyer) // panics if Buyer is nil
}

func safeProcess(inv Invoice) (ok bool) {
	defer func() {
		if r := recover(); r != nil {
			fmt.Println("skipped", inv.ID, "because:", r)
			ok = false
		}
	}()
	process(inv)
	return true
}

func main() {
	b := "Sharma Traders"
	invoices := []Invoice{{"INV-1", &b}, {"INV-2", nil}, {"INV-3", &b}}
	for _, inv := range invoices {
		safeProcess(inv)
	}
}
INV-2 is skipped with "invalid memory address or nil pointer dereference"; INV-1 and INV-3 are processed. The real fix is still to correct the bug.

Practice (0/4 done)

Task 1 · Question

What does this print? func main() { defer fmt.Println("bye") panic("oops") }

Task 2 · Question

Where must recover() be called to stop a panic?

Task 3 · Question

A user types a date in the wrong format. What should your parse function do?

Task 4 · Practice

Write func safeGet(s []int, i int) (v int, err error) that returns s[i], but converts an out-of-range panic into an error using defer and recover. Then write a second version that avoids panic entirely by checking the index first, and explain in one line which is better.

Quick quiz

Question 1

1.What runs when a function panics?

Question 2

2.Why does regexp.MustCompile panic instead of returning an error?

Question 3

3.A goroutine you started panics and has no recover. What happens?

Question 4

4.What does net/http do if one of your handlers panics?

Read more (free)

Open a question to see the answer and why. The first ones are this topic's own quiz, so try the quiz above before you peek.

What runs when a function panics?

Answer: Its deferred calls, then the callers' deferred calls

The panic unwinds the stack, running deferred calls as it goes.

Why does regexp.MustCompile panic instead of returning an error?

Answer: It is meant for fixed patterns in your code, where a bad pattern is a programming mistake

Must helpers are for constant inputs; failures indicate bugs.

A goroutine you started panics and has no recover. What happens?

Answer: The whole program crashes

An unrecovered panic in any goroutine terminates the program.

What does net/http do if one of your handlers panics?

Answer: It recovers, logs the panic and closes that connection

The HTTP server recovers handler panics so one request cannot kill the server.

How do you see the chain of calls that led to a panic?Related

Answer: Set RUST_BACKTRACE=1

The backtrace lists the function calls.

From panic! and unrecoverable errors

Do deferred calls run after os.Exit(1)?Related

Answer: No, os.Exit ends the program immediately

os.Exit skips deferred calls; so does log.Fatal, which calls os.Exit.

From defer: clean up at the end

Where should panic-recovery middleware go in the chain?Related

Answer: Outermost

Outermost catches panics from all inner middleware and handlers.

From Middleware: logging, recovery and request IDs

What happens if a handler throws and nothing catches it?Related

Answer: The request can hang or the server can crash

Uncaught errors can stop the request or the process. Wrap handlers in try and catch.

From Handling errors well

Pass the quiz (60% or more) or mark this topic as done when you are ready.

Your progress is saved on this device only.

Next steps

  1. This is the last topic of “Pointers, interfaces and errors”. The module exam comes next.
  2. Module 4 exam: Pointers, interfaces and errors · 14 questions · 25 min, without help. 5 topics of this module still to finish first.
  3. Then the next module, Packages, testing and generics. 46 topics left in the course before the final exam.

← Previous: Errors as values: wrap, Is and As