Go Gopher How to Go

Panic is a built-in function in Go that stops normal execution flow and begins panicking. When a function panics, it immediately stops executing, runs any deferred functions, and then returns to its caller. This process continues up the stack until all functions in the current goroutine have returned, at which point the program crashes.

💡 Key Points

  • Panic stops normal execution and unwinds the stack
  • Deferred functions still execute during a panic
  • Use panic for truly unrecoverable errors
  • Prefer returning errors over panicking in most cases
  • Panic can be recovered using recover() in deferred functions
  • Unrecovered panics crash the program with a stack trace
  • Panics in goroutines only affect that goroutine (unless unrecovered)

Basic Panic Usage

A panic is typically caused by programming errors that should not occur:

package main

import "fmt"

func main() {
    fmt.Println("starting")
    panic("something bad happened")
    fmt.Println("this will not print")
}
Output:
starting
panic: something bad happened

goroutine 1 [running]: 
main.main()
        /path/to/file.go:6 +0x... 

Panic with Defer

Deferred functions execute even when a panic occurs:

package main

import "fmt"

func main() {
    defer fmt.Println("deferred call 1")
    defer fmt.Println("deferred call 2")
    
    fmt.Println("about to panic")
    panic("panic occurred")
    
    fmt.Println("this will not execute")
}
Output:
about to panic
deferred call 2
deferred call 1
panic: panic occurred

goroutine 1 [running]:
main. main()
        /path/to/file.go:9 +0x...

Recovering from Panic

The recover function can catch a panic and resume normal execution:

package main

import "fmt"

func safeDivide(a, b int) (result int) {
    defer func() {
        if r := recover(); r != nil {
            fmt. Println("recovered from panic:", r)
            result = 0
        }
    }()
    
    result = a / b
    return result
}

func main() {
    fmt.Println("result:", safeDivide(10, 2))
    fmt.Println("result:", safeDivide(10, 0))
    fmt.Println("program continues normally")
}
Output:
result: 5
recovered from panic: runtime error: integer divide by zero
result: 0
program continues normally

When to Panic

ScenarioUse Panic? Reason
Unrecoverable errors during init✅ YesCannot continue if setup fails
Programming errors (bugs)✅ YesIndicates code needs fixing
Impossible state reached✅ YesLogic error in code
Expected errors (file not found)❌ NoReturn error instead
User input validation failures❌ NoExpected scenario, return error
Library/API code❌ NoLet caller decide how to handle

Panic Functions and Methods

FunctionSignatureDescription
panic panic(v interface{})Stops execution and begins panicking
recoverrecover() interface{}Regains control of panicking goroutine

Common Panic Causes

CauseExamplePrevention
Nil pointer dereferencevar p *int; fmt.Println(*p) Check for nil before dereferencing
Index out of rangearr := []int{1,2,3}; arr[5]Check length before accessing
Type assertion failurevar i interface{} = "hello"; i.(int)Use comma-ok idiom: v, ok := i.(int)
Close on closed channelclose(ch); close(ch)Track channel state, close once
Send on closed channelclose(ch); ch <- 1Coordinate sender/receiver properly
Division by zerox / 0Check divisor before operation
Map concurrent accessConcurrent read/write to mapUse sync.Map or mutex protection

Panic and Recover Patterns

PatternCodeUse Case
Basic Recoverydefer func() { if r := recover(); r != nil { log.Println(r) } }()Log and continue
Convert to Errordefer func() { if r := recover(); r != nil { err = fmt.Errorf("%v", r) } }()Convert panic to error return
Selective Recoverydefer func() { if r := recover(); r != myError { panic(r) } }()Only catch specific panics
Cleanup and Re-panicdefer func() { cleanup(); panic(recover()) }()Cleanup resources then propagate panic
Goroutine Protectiongo func() { defer recover(); ... }()Prevent goroutine panic from crashing program
Assert Invariantsif !condition { panic("invariant violated") }Check programming assumptions

Panic Best Practices

PracticeDescriptionExample
Prefer ErrorsReturn errors instead of panickingreturn nil, errors.New("failed") not panic(... )
Document PanicsDocument when functions may panicComment: "Panics if index out of range"
Recover in GoroutinesAlways recover in goroutines you startPrevents unhandled panic crashes
Recover at BoundariesRecover at package/API boundariesConvert to errors for callers
Don't Recover EverywhereLet panics crash during developmentReveals bugs that need fixing
Stack TracesInclude stack trace when recoveringdebug.Stack() for context