Error Handling

Table of Contents

Error Handling Strategies tutorial

Consider following options:

  1. To propagate the error, so that a failure in a subroutine becomes a failure of the calling routine. Use fmt.Errorf, which formats an error message using fmt.Sprintf and returns a new error value.

    genesis: crashed: no parachute: G-switch failed: bad relay orientation
    

    Because error messages are frequently chained together, message strings should not be capitalized and newlines should be avoided.

  2. To retry the failed operation after some delay, if the failure is transient.
  3. To report the error and stop the program gracefully. Consider using log.Fatalf(), which reports the error and exits the program.
  4. To just log the error and then continue.

Even though it is idiomatic to return error whenever possible, if you think that you can not handle the error, you can use panic() as follows:

package main

import "os"

func main() {
    _, err := os.Create("/tmp/file")
    if err != nil {
        panic(err)
    }
}

When panic() is called, all deferred functions are called and then the program will be terminated. You can call recover() during the deferred functional call phase, so that the program won't be terminated. It is generally not recommended to recover some other packages' panic state.

recover() reference

How recover() works discussion

  1. When panic() is executed, go tries to execute all deferred functions within the callee function before exiting it.
  2. If go managed to execute recover() While executing deferred functions,
    1. It sets the program to a normal state, which means that panicking will be no longer propagated.
    2. After executing remaining deferred functions, it just exits the current function by returning the current value of result parameters.
    3. So, by modifying named result parameters, you can return different values when recovering.
package main

import "fmt"

func f() (result int) {
    defer func() {
        // This is the idiomatic way to call `recover()`
        if r := recover(); r != nil {
            fmt.Println("Recovering")
            // Assign a different value to the result parameter
            result = -1
        }
    }()

  result = 0
    fmt.Println("Before panic()")
    panic("Opps!")
    fmt.Println("After panic()")
    return
}

func main() {
    fmt.Println("Before f()")
    result := f()
    fmt.Println("After f()")
    fmt.Println(result)
}
Before f()
Before panic()
Recovering
After f()
-1