In Go, panic is used to signal a fatal error in the program. After a panic is invoked, the execution begins to unwind the stack and call all deferred functions. If there is a function in the stack that has called recover(), it can intercept and handle the panic — but recover only works within its own goroutine and only if called from defer.
The recommended pattern for safe recovery:
func safe(fn func()) { defer func() { if r := recover(); r != nil { fmt.Println("Recovered from panic:", r) } }() fn() } go safe(func() { panic("fail!") // does not stop the program })
It's important to remember that:
Can recover catch a panic that occurred in another goroutine?
Answer: No, recover only works within the goroutine where the panic occurred, and only if invoked in a deferred function. If a panic originated in one goroutine and recover is called in another — interception will not occur, and the program will terminate abnormally.
Example:
func main() { go func() { panic("inside goroutine") }() time.Sleep(time.Second) recover() // Will not work! Panic will terminate the program }
Story
A developer implemented error handling using the pattern defer recover() only in the main function. When a panic occurred inside a worker in a separate goroutine, the entire program crashed — the error was not caught by the global recover.
Story
In a project, defer/recover was used for graceful shutdown of the web server, believing that this would suffice. It turned out that a panic in one of the goroutines finished the entire process because the recover handling was not in the right place — the entire worker pool had to be refactored.
Story
In the message processing thread, a panic occurred due to incorrect operation of a user function. The developer expected that it would be caught by the top-level recover, but did not realize that recover works ONLY from defer. As a result, the service crashed irregularly when an invalid message appeared in the queue.