Error handling in Golang
Tags: go, webdev
Published: 2019-10-14 12:51:54

---
title: Error handling in Golang
published: true
date: 2019-10-13 22:00:56 UTC
tags: go, webdev
canonical_url: https://blog.logrocket.com/error-handling-in-golang/
cover_image: https://thepracticaldev.s3.amazonaws.com/i/rlz9bmqcokln7bn1ztis.png
---
**Written by [Raphael Ugwu](https://blog.logrocket.com/author/raphaelugwu/)**✏️

Unlike conventional methods in other mainstream programming languages such as JavaScript (which uses the `try… catch` statement) or Python (with its `try… except` block) tackling errors in Go requires a different approach. Why? Because its features for error handling are often misapplied.

In this blog post, we’ll take a look at the best practices that could be used to handle errors in a Go application. A basic understanding of how [Go](https://golang.org/doc/) works is all that is required to digest this article – should you feel stuck at some point, it’s okay to take some time and research unfamiliar concepts.

## The blank identifier

[The blank identifier](https://golang.org/doc/effective_go.html#blank) is an anonymous placeholder. It may be used like any other identifier in a declaration, but it does not introduce a binding. The blank identifier provides a way to ignore left-handed values in an assignment and avoid compiler errors about unused imports and variables in a program. The practice of assigning errors to the blank identifier instead of properly handling them is unsafe as this means you have decided to explicitly ignore the value of the defined function.

```jsx
result, _ := iterate(x,y)

if value > 0 {
  // ensure you check for errors before results.
}
```

Your reason for probably doing this is that you’re not expecting an error from the function (or whatever error may occur) but this could create cascading effects in your program. The best thing to do is to handle an error whenever you can.

[![LogRocket Free Trial Banner](https://i0.wp.com/blog.logrocket.com/wp-content/uploads/2017/03/f760c-1gpjapknnuyhu8esa3z0jga.png?resize=1200%2C280&ssl=1)](https://logrocket.com/signup/)

## Handling errors through multiple return values

One way to handle errors is to take advantage of the fact that functions in Go support multiple return values. Thus you can pass an error variable alongside the result of the function you’re defining:

```jsx
func iterate(x, y int) (int, error) {

}
```

In the code sample above, we have to return the predefined `error` variable if we think there’s a chance our function may fail. `error` is an interface type declared in Go’s `built-in` package and its zero value is `nil`.

```jsx
type error interface {
   Error() string
 }
```

Usually, returning an error means there’s a problem and returning `nil` means there were no errors:

```jsx
result, err := iterate(x, y)
 if err != nil {
  // handle the error appropriately
 } else {
  // you're good to go
 }
```

Thus whenever the function `iterate` is called and `err` is not equal to `nil`, the error returned should be handled appropriately – an option could be to create an instance of a retry or cleanup mechanism. The only drawback with handling errors this way is that there’s no enforcement from Go’s compiler, you have to decide on how the function you created returns the error. You can define an error struct and place it in the position of the returned values. One way to do this is by using the built-in `errorString` struct (you can also find this code at [Go’s source code](https://golang.org/src/errors/errors.go)):

```jsx
package errors

 func New(text string) error {
     return &errorString {
         text
     }
 }

 type errorString struct {
     s string
 }

 func(e * errorString) Error() string {
     return e.s
 }
```

In the code sample above, `errorString` embeds a `string` which is returned by the `Error` method. To create a custom error, you’ll have to define your error struct and use [method sets](https://golang.org/ref/spec#Method_sets) to associate a function to your struct:

```jsx
// Define an error struct
type CustomError struct {
    msg string
}
// Create a function Error() string and associate it to the struct.
func(error * CustomError) Error() string {
    return error.msg
}
// Then create an error object using MyError struct.
func CustomErrorInstance() error {
    return &CustomError {
        "File type not supported"
    }
}
```

The newly created custom error can then be restructured to use the built-in `error` struct:

```jsx
 import "errors"
func CustomeErrorInstance() error {
    return errors.New("File type not supported")
}
```

One limitation of the built-in `error` struct is that it does not come with stack traces. This makes locating where an error occurred very difficult. The error could pass through a number of functions before it gets printed out. To handle this, you could install the `pkg/errors` package which provides basic error handling primitives such as stack trace recording, error wrapping, unwrapping, and formatting. To install this package, run this command in your terminal:

```jsx
go get github.com/pkg/errors
```

When you need to add stack traces or any other information that makes debugging easier to your errors, use the `New` or `Errorf` functions to provide errors that record your stack trace. `Errorf` implements the [`fmt.Formatter`](https://golang.org/pkg/fmt/#Formatter) interface which lets you format your errors using the `fmt` package runes (`%s`, `%v`, `%+v` etc):

```jsx
import(
    "github.com/pkg/errors"
    "fmt"
)
func X() error {
    return errors.Errorf("Could not write to file")
}

func customError() {
    return X()
}

func main() {
    fmt.Printf("Error: %+v", customError())
}
```

To print stack traces instead of a plain error message, you have to use `%+v`  instead of `%v` in the format pattern, and the stack traces will look similar to the code sample below:

```jsx
Error: Could not write to file
main.X
 /Users/raphaelugwu/Go/src/golangProject/error_handling.go:7
main.customError
 /Users/raphaelugwu/Go/src/golangProject/error_handling.go:15
main.main
 /Users/raphaelugwu/Go/src/golangProject/error_handling.go:19
runtime.main
 /usr/local/opt/go/libexec/src/runtime/proc.go:192
runtime.goexit
 /usr/local/opt/go/libexec/src/runtime/asm_amd64.s:2471
```

## Defer, panic, and recover

Although Go doesn’t have exceptions, it has a similar kind of mechanism known as “[Defer, panic, and recover](https://blog.golang.org/defer-panic-and-recover)“. Go’s ideology is that adding exceptions such as the `try/catch/finally` statement in JavaScript would result in complex code and encourage programmers to label too many basic errors, such as failing to open a file, as exceptional. You should not use `defer/panic/recover` as you would `throw/catch/finally`; only in cases of unexpected, unrecoverable failure.

`Defer` is a language mechanism that puts your function call into a stack. Each deferred function is executed in reverse order when the host function finishes regardless of whether a panic is called or not. The defer mechanism is very useful for cleaning up resources:

```jsx
package main

import (
        "fmt"
)

func A() {
        defer fmt.Println("Keep calm!")
        B()
}
func B() {
        defer fmt.Println("Else...")
        C()
}
func C() {
        defer fmt.Println("Turn on the air conditioner...")
        D()
}
func D() {
        defer fmt.Println("If it's more than 30 degrees...")
}
func main() {
        A()
}
```

This would compile as:

```jsx
If it's more than 30 degrees...
Turn on the air conditioner...
Else...
Keep calm!
```

`Panic` is a built-in function that stops the normal execution flow. When you call `panic` in your code, it means you’ve decided that your caller can’t solve the problem. Thus `panic` should only be used in rare cases where it’s not safe for your code or anyone integrating your code to continue at that point. Here’s a code sample depicting how `panic` works:

```jsx
package main

import (
        "errors"
        "fmt"
)

func A() {
        defer fmt.Println("Then we can't save the earth!")
        B()
}
func B() {
        defer fmt.Println("And if it keeps getting hotter...")
        C()
}
func C() {
        defer fmt.Println("Turn on the air conditioner...")
        Break()
}
func Break() {
        defer fmt.Println("If it's more than 30 degrees...")
        panic(errors.New("Global Warming!!!"))

}
func main() {
        A()
}
```

The sample above would compile as:

```jsx
If it's more than 30 degrees...
Turn on the air conditioner...
And if it keeps getting hotter...
Then we can't save the earth!
panic: Global Warming!!!

goroutine 1 [running]:
main.Break()
        /tmp/sandbox186240156/prog.go:22 +0xe0
main.C()
        /tmp/sandbox186240156/prog.go:18 +0xa0
main.B()
        /tmp/sandbox186240156/prog.go:14 +0xa0
main.A()
        /tmp/sandbox186240156/prog.go:10 +0xa0
main.main()
        /tmp/sandbox186240156/prog.go:26 +0x20

Program exited: status 2.
```

As shown above, when `panic` is used and not handled, the execution flow stops, all deferred functions are executed in reverse order and stack traces are printed.

You can use the `recover` built-in function to handle `panic` and return the values passing from a panic call. `recover` must always be called in a `defer` function else it will return `nil`:

```jsx
package main

import (
        "errors"
        "fmt"
)

func A() {
        defer fmt.Println("Then we can't save the earth!")
        defer func() {
                if x := recover(); x != nil {
                        fmt.Printf("Panic: %+v\n", x)
                }
        }()
        B()
}
func B() {
        defer fmt.Println("And if it keeps getting hotter...")
        C()
}
func C() {
        defer fmt.Println("Turn on the air conditioner...")
        Break()
}
func Break() {
        defer fmt.Println("If it's more than 30 degrees...")
        panic(errors.New("Global Warming!!!"))

}
func main() {
        A()
}
```

As can be seen in the code sample above, `recover` prevents the entire execution flow from coming to a halt because we threw in a `panic` function and the compiler would return:

```jsx
If it's more than 30 degrees...
Turn on the air conditioner...
And if it keeps getting hotter...
Panic: Global Warming!!!
Then we can't save the earth!

Program exited.
```

To report an error as a return value, you have to call the `recover` function in the same [goroutine](https://golangbot.com/goroutines/) as the `panic` function is called, retrieve an error struct from the `recover` function, and pass it to a variable:

```jsx
package main

import (
        "errors"
        "fmt"
)

func saveEarth() (err error) {

        defer func() {
                if r := recover(); r != nil {
                        err = r.(error)
                }
        }()
        TooLate()
        return
}
func TooLate() {
        A()
        panic(errors.New("Then there's nothing we can do"))
}

func A() {
        defer fmt.Println("If it's more than 100 degrees...")
}
func main() {
        err := saveEarth()
        fmt.Println(err)
}
```

Every deferred function will be executed after a function call but before a return statement. So, you can set a returned variable before a return statement gets executed. The code sample above would compile as:

```jsx
If it's more than 100 degrees...
Then there's nothing we can do

Program exited.
```

## Error wrapping

Previously error wrapping in Go was only accessible via using packages such as `pkg/errors`. However, with Go’s latest release – [version 1.13](https://golang.org/doc/go1.13), support for error wrapping is present. According to the [release notes](https://golang.org/doc/go1.13#error_wrapping):

> An error `e` can wrap another error `w` by providing an `Unwrap` method that returns `w`. Both `e` and `w` are available to programs, allowing `e` to provide additional context to `w` or to reinterpret it while still allowing programs to make decisions based on `w`.

To create wrapped errors, `fmt.Errorf` now has a `%w` verb and for inspecting and unwrapping errors, a couple of functions have been added to the `error` package:

`errors.Unwrap`: This function basically inspects and exposes the underlying errors in a program. It returns the result of calling the `Unwrap` method on `Err`. If Err’s type contains an `Unwrap` method returning an error. Otherwise, `Unwrap` returns `nil`.

```jsx
package errors

type Wrapper interface{
  Unwrap() error
}
```

Below is an example implementation of the `Unwrap` method:

```jsx
func(e*PathError)Unwrap()error{
  return e.Err
}
```

`errors.Is`: With this function, you can compare an error value against the sentinel value. What makes this function different from our usual error checks is that instead of comparing the sentinel value to one error, it compares it to every error in the error chain. It also implements an `Is` method on an error so that an error can post itself as a sentinel even though it’s not a sentinel value.

```jsx
func Is(err, target error) bool
```

In the basic implementation above, `Is` checks and reports if `err` or any of the `errors` in its chain are equal to target (sentinel value).

`errors.As`: This function provides a way to cast to a specific error type. It looks for the first error in the error chain that matches the sentinel value and if found, sets the sentinel value to that error value and returns `true`:

```jsx
package main

import (
        "errors"
        "fmt"
        "os"
)

func main() {
        if _, err := os.Open("non-existing"); err != nil {
                var pathError *os.PathError
                if errors.As(err, &pathError) {
                        fmt.Println("Failed at path:", pathError.Path)
                } else {
                        fmt.Println(err)
                }
        }

}
```

You can find this code in [Go’s source code](https://golang.org/pkg/errors/#As).

Compiler result:

```jsx
Failed at path: non-existing

Program exited.
```

An error matches the sentinel value if the error’s concrete value is assignable to the value pointed to by the sentinel value. `As` will panic if the sentinel value is not a non-nil pointer to either a type that implements error or to any interface type. `As` returns false if `err` is `nil`.

## Summary

The Go community has been making impressive strides as of late with [support for various programming concepts and introducing](https://jaxenter.com/golang-version-1-13-161693.html) even more concise and easy ways to handle errors. Have you got any ideas on how to handle or work with errors that may appear in your Go program? Do let me know in the comments below.

**Resources:**  
[Go’s programming language specification on Type assertion](https://golang.org/ref/spec#Type_assertions)  
[Marcel van Lohuizen’s talk at dotGo 2019 – Go 2 error values today](https://youtu.be/SeVxmQl9Wmk)  
[Go 1.13 release notes](https://golang.org/doc/go1.13)
* * * 
**Editor's note:** Seeing something wrong with this post? You can find the correct version [here](https://blog.logrocket.com/error-handling-in-golang/).

## Plug: [LogRocket](https://logrocket.com/signup/), a DVR for web apps
 
![LogRocket Dashboard Free Trial Banner](https://i2.wp.com/blog.logrocket.com/wp-content/uploads/2017/03/1d0cd-1s_rmyo6nbrasp-xtvbaxfg.png?resize=1200%2C677&ssl=1)
 
[LogRocket](https://logrocket.com/signup/) is a frontend logging tool that lets you replay problems as if they happened in your own browser. Instead of guessing why errors happen, or asking users for screenshots and log dumps, LogRocket lets you replay the session to quickly understand what went wrong. It works perfectly with any app, regardless of framework, and has plugins to log additional context from Redux, Vuex, and @ngrx/store.
 
In addition to logging Redux actions and state, LogRocket records console logs, JavaScript errors, stacktraces, network requests/responses with headers + bodies, browser metadata, and custom logs. It also instruments the DOM to record the HTML and CSS on the page, recreating pixel-perfect videos of even the most complex single-page apps.
 
[Try it for free](https://logrocket.com/signup/).
* * *
The post [Error handling in Golang](https://blog.logrocket.com/error-handling-in-golang/) appeared first on [LogRocket Blog](https://blog.logrocket.com).