DEV Community

Joy Biswas
Joy Biswas

Posted on

Why You Should Use Panic Instead of Fatal for Cleanup

When writing code in Go we often run into errors that are critical. These are errors that mean our application cannot continue running. To handle these we usually look at two options. We can use panic or we can use log.Fatal.

They both stop the application but they do it in very different ways. The main difference is how they treat the defer function.

The Problem with Fatal

When you use log.Fatal the application stops immediately. It calls a system function called os.Exit(1). This is a hard stop. The program does not look back and it does not run any cleanup code.

Let us look at a standard example. Imagine we are connecting to a database in our main function. We want to make sure the database connection closes when the app stops.

package main

import (
    "fmt"
    "log"
)

func main() {
    fmt.Println("1. Opening Database Connection...")

    // We schedule this to run when the function exits
    defer fmt.Println("Clean up: Closing Database Connection.")

    // Something bad happens here
    log.Fatal("CRITICAL ERROR: System failure!")
}
Enter fullscreen mode Exit fullscreen mode

Output:

1. Opening Database Connection...
2025/11/26 10:00:00 CRITICAL ERROR: System failure!
exit status 1
Enter fullscreen mode Exit fullscreen mode

Notice what is missing? The line "Clean up: Closing Database Connection" never printed. The defer function was ignored. If this was a real application the database connection might remain open on the server side until it times out because we never sent the close signal.

Why Panic is Different

Now let us look at panic. When a program panics it begins to shut down but it does not stop immediately. It starts "unwinding" the stack. This means it goes back up through the functions you called.

Crucially, panic executes any deferred functions it finds along the way.

Here is the same example using panic:

package main

import (
    "fmt"
)

func main() {
    fmt.Println("1. Opening Database Connection...")

    // We schedule this to run when the function exits
    defer fmt.Println("Clean up: Closing Database Connection.")

    // Something bad happens here
    panic("CRITICAL ERROR: System failure!")
}
Enter fullscreen mode Exit fullscreen mode

Output:

1. Opening Database Connection...
Clean up: Closing Database Connection.
panic: CRITICAL ERROR: System failure!

[stack trace...]
Enter fullscreen mode Exit fullscreen mode

The Logic: Why Use Panic?

You should use panic instead of log.Fatal when you have critical resources to manage.

In our example we initialized our application and opened a main database connection. The connection was successful. But then another error occurred later in the code.

If we use log.Fatal the app just dies. The database connection is never explicitly closed.

If we use panic the application understands that it needs to crash but it is polite about it. It pauses to run the defer function. This allows us to strictly close the database connection. Once the cleanup is done the application finally shuts down and prints the error message.

Summary

  • log.Fatal: Stops the app instantly. It skips defer functions. Use this only if you have no cleanup to do.
  • panic: Stops the normal flow but runs defer functions first. Use this when you need to ensure connections, files, or resources are closed properly before the program exits.

Top comments (0)