Blog |

How to Wrap and Unwrap Errors in Golang

How to Wrap and Unwrap Errors in Golang
Table of Contents

In Golang, wrapping errors means adding more contextual information to the error which has been returned. For example, the additional information could be the type of error, the cause of the error, or the name of the function where the error is raised.

Wrapping is very useful for debugging since you can precisely and quickly locate the source of the problem.

Golang supports wrapping and unwrapping errors as part of the standard library errors by using the errors.Unwrap() and fmt.Errorf() functions with the %w verb.

Syntax for wrapping an error in Golang

First we need to create a new error using errors.New() followed by fmt.Errorf() with the %w verb to wrap the error.

var criticalError = errors.New("Serious error")
.....

wrap = fmt.Errorf("...%w...",criticalError,...)

The wrapped error can be unwrapped using the errors.Unwrap() function.

func Unwrap(err error) error

Example 1: How to Wrap An Error

In the below code, we have added a function validations()which takes a number and runs validations based on some conditions by again calling the check() function. Whenever an error is encountered, it will wrap the error using fmt.Errorf() and the %w verb to show that “run error” has occurred. The new error is then returned.

package main
import (
    "errors"
    "fmt"
)

var (
    errUhOh  = errors.New("oh critical error!!")
)

func check(num int) error {
    if num == 1 {
        return fmt.Errorf("it's odd")
    } else if num == 2 {
        return errUhOh 
    }
    return nil
}

func validations(num int) error{

err := check(num)
    if err != nil {
        return fmt.Errorf("run error: %w", err)
    }
    return nil
}

func main() {
    for num := 1; num <= 5; num++ {
        fmt.Printf("validating %d... ", num)
        err := validations(num)
        if err == errUhOh {
            fmt.Println("oh no something has happened!")
        } else if err != nil {
            fmt.Println("some error is present...", err)
        } else {
            fmt.Println("valid number only...!")
        }
    }
}

When the above code is run we get the following output:

validating 1... some error is present... run error: it's odd
validating 2... some error is present... run error: oh critical error!!
validating 3... valid number only...!
validating 4... valid number only...!
validating 5... valid number only...!

If we observe the output, the error message with value 1 includes “run error: it’s odd”. This signifies that the error message was wrapped using fmt.Errorf() in the validations() function. The value, “it's odd”, is additional context or information added to the error.

However, there is a minor problem with the code; if we look at the output, we can see that while validating input 2, i.e when the loop is run for the second time inside our main function, we receive the standard error message, “run error: oh, critical error!!” instead of a notification saying “oh no, something has happened!”.The check() function func check(num int) error is still returning “oh critical error!!” but in case of errUhoh error, detection is broken.

This happens because the error returned by validations() is no longer errUhOh. Instead, it's the wrapped error created by fmt.Errorf() within the main()function. In the if condition, when the err variable is compared to errUhOh, it returns false as err isn't equal to errUhOh any more, it is equal to the error that's wrapping errUhOh.

Now to fix the error checking of errUhOh, we need to fetch what's inside the wrapper. Therefore, we need to unwrap it using the errors.Unwrap() function.

Example 2: How to Unwrap An Error

Here we will extend the above example and use errors.Unwrap function:

package main
import (
    "errors"
    "fmt"
)

var (
    errUhOh  = errors.New("oh critical error!!")
)

func check(num int) error {
    if num == 1 {
        return fmt.Errorf("it's odd")
    } else if num == 2 {
        return errUhOh 
    }
    return nil
}

func validations(num int) error{

err := check(num)
    if err != nil {
        return fmt.Errorf("run error: %w", err)
    }
    return nil
}

func main() {
    for num := 1; num <= 5; num++ {
        fmt.Printf("validating %d... ", num)
        err := validations(num)
        if err == errUhOh || errors.Unwrap(err) == errUhOh {
            fmt.Println("oh no something has happened!")
        } else if err != nil {
            fmt.Println("some error is present...", err)
        } else {
            fmt.Println("valid number only...!")
        }
    }
}

Output:

validating 1... some error is present: run error: it's odd
validating 2... oh no something has happened!
validating 3... valid number only...!

Now you know how to encapsulate an error using the %w verb within fmt.Errorf() and check the contents of the wrapped error by unwrapping it with the help of the errors.Unwrap() function.

Track, Analyze and Manage Errors With Rollbar

Managing errors and exceptions in your code is challenging. It can make deploying production code an unnerving experience. Being able to track, analyze, and manage errors in real-time can help you proceed with more confidence. Rollbar automates error monitoring and triaging, making fixing Golang errors easier than ever. Try it today!

Related Resources

"Rollbar allows us to go from alerting to impact analysis and resolution in a matter of minutes. Without it we would be flying blind."

Error Monitoring

Start continuously improving your code today.

Get Started Shape