A panic in Go is an abrupt interruption of the normal program flow. It exists, yes, but it does not replace normal error handling.
In the previous article, we saw that in Go errors are values and that we must handle them with if err != nil. That’s the civilized way to work.
What happens if the program tries to access an invalid index or a library detects an impossible internal state?
In those cases, the goroutine enters a panic state and begins to unwind its call stack.
Let’s see what happens during that process and when it makes sense to trigger a panic explicitly. In the following article, we will look at recover in detail.
What Happens During a panic
When a panic occurs, the normal execution flow stops immediately.
The current function is interrupted.
All deferred functions (defer) of that function are executed.
The panic “bubbles up” the call stack (executing the defer at each level).
If no one recovers it in that goroutine, the program terminates and displays the stack trace.
:::
Automatic Panics
Go automatically triggers panics for unrecoverable programming errors:
- Accessing an index out of range in an array.
- Writing to a
nilmap. - Dereferencing a
nilpointer or selecting one of its fields.
func main() {
var slice []int
// This causes a panic runtime error: index out of range
fmt.Println(slice[0])
// This line will NEVER execute
fmt.Println("End of program")
}Manual Panics
We can also press the red button ourselves using the panic() function. It accepts any value (typically a string or an error).
func InitSystem() {
if !CheckConfig() {
// If there's no configuration, we can't start. Abort.
panic("corrupt configuration: unable to start")
}
}When to Use panic
This is the point where Java/C# programmers often get it wrong. They see panic/recover and think: “Ah, this is throw/catch”.
It is not. In Go, using panic for normal flow control is considered a bad practice and poor design.
When Not to Use It
- Validation errors: The user enters an invalid email. (Use
error). - Files not found: (Use
error). - Network failures: (Use
error). - Anything you can foresee: If it can possibly fail, return an
error.
A library should not panic due to invalid input that could occur during normal use. In that case, it should return an error.
When It Might Make Sense
Panic is reserved for truly exceptional errors or programmer bugs:
- Initialization that cannot fail by design: functions like
Must...typically trigger apanicwhen a constant or a configuration provided by the programmer is invalid. - Serious internal inconsistency: If your code reaches a
switchbranch that is theoretically impossible to hit. - Bounded internal logic: Some packages use
panicwithin an algorithm and convert it to anerrorbefore crossing the public boundary. The caller should never see it.
panic should not be used to save ourselves from an if err != nil. To decide what to do at the boundary of a goroutine, there is recover, which we will cover next.