#Joining Errors With errors.Join in Go
In Go 1.20, a powerful new feature was introduced: errors.Join. This allows you to combine multiple errors into a single error value, making it easier to handle and report on multiple failures that occur during the execution of your code.
This is super handy if you want to enrich the errors that may be getting returned from parts of your Go application and allows you to do better error checking further down the chain.
When this was announced, it was a fantastic addition to the Go programming language that simplifies error handling patterns significantly.
Understanding errors.Join
The errors.Join function takes multiple errors and returns a single error that represents all of them. This is particularly useful when you need to collect multiple failures from operations and report them together.
Here’s the basic syntax:
func Join(errs ...error) error
It accepts any number of error values and returns a single error that contains all of them.
Basic Example
Let’s start with a simple example where we perform multiple operations that might fail:
package main
import (
"errors"
"fmt"
)
func performOperations() error {
var err1 error = errors.New("operation 1 failed")
var err2 error = errors.New("operation 2 failed")
var err3 error = nil
// Join all the errors together
return errors.Join(err1, err2, err3)
}
func main() {
err := performOperations()
if err != nil {
fmt.Println("Errors occurred:")
fmt.Println(err)
}
}
When you run this, the output will display all the errors that were joined together:
Errors occurred:
operation 1 failed
operation 2 failed
Practical Use Case: Multiple Cleanup Operations
A common scenario where errors.Join is useful is when you need to perform multiple cleanup operations and want to collect any errors that occur:
package main
import (
"errors"
"fmt"
)
func closeResource(name string) error {
// Simulate closing a resource
if name == "database" {
return errors.New("failed to close database connection")
}
return nil
}
func cleanup() error {
var errs []error
// Close multiple resources
if err := closeResource("database"); err != nil {
errs = append(errs, err)
}
if err := closeResource("cache"); err != nil {
errs = append(errs, err)
}
if err := closeResource("file"); err != nil {
errs = append(errs, err)
}
// Join all errors into one
return errors.Join(errs...)
}
func main() {
if err := cleanup(); err != nil {
fmt.Println("Cleanup failed:")
fmt.Println(err)
}
}
Checking for Specific Errors
You can use errors.Is() to check if a specific error is contained within a joined error:
package main
import (
"errors"
"fmt"
)
var ErrDatabase = errors.New("database error")
var ErrNetwork = errors.New("network error")
func main() {
err := errors.Join(
ErrDatabase,
ErrNetwork,
errors.New("unknown error"),
)
// Check if specific errors are in the joined error
if errors.Is(err, ErrDatabase) {
fmt.Println("Database error detected")
}
if errors.Is(err, ErrNetwork) {
fmt.Println("Network error detected")
}
}
Iterating Over Joined Errors
While errors.Join combines errors, you can’t directly iterate over them. However, you can use errors.Unwrap() or type assertion to work with the underlying error structure if needed for advanced use cases.
Conclusion
The errors.Join function is a powerful addition to Go’s error handling toolkit. It simplifies scenarios where you need to collect and report multiple errors without losing information about any of them. Whether you’re performing cleanup operations, validating multiple conditions, or handling concurrent operations, errors.Join provides a clean and idiomatic way to combine errors.
By using this feature effectively, you can write more robust Go applications with better error handling and reporting.
Further Reading
If you enjoyed this, you may like my other articles on error handling in Go:
Continue Learning
Go 1.23 Iterators Tutorial
In this tutorial, we'll be exploring the new range-over-func syntax introduced in Go 1.23 and how to use iterators in your Go applications.
Functional Options Parameter Pattern in Go
In this tutorial, we'll be discussing one of my favorite patterns and how you can use it to supercharge your Go app dev.
Getting Started With Testmain in Go
In this tutorial, we'll be covering how you can simplify and improve your tests in Go using the TestMain function.
Creating a RESTful API With Golang
this tutorial demonstrates how you can create your own simple RESTful JSON api using Go(Lang)