Skip to main content

Collect concurrent errors with Group

When you need to run multiple operations concurrently and collect any errors they produce, you can use a multierror.Group. This provides a straightforward way to manage a set of goroutines, wait for them to finish, and consolidate their results.

Running Concurrent Operations

The primary pattern is to create a multierror.Group, schedule functions to run using the Go receiver method, and then call the Wait receiver method to block until all functions have completed. Each call to Go launches a new goroutine to execute the provided function.

If all scheduled functions execute successfully and return nil, Wait will also return nil. You must check the result of Wait to confirm that no errors occurred.

package main

import (
"sync/atomic"

"github.com/hashicorp/go-multierror"
)

func main() {
var group multierror.Group
var ran atomic.Int32
group.Go(func() error { ran.Add(1); return nil })
group.Go(func() error { ran.Add(1); return nil })
result := group.Wait()
if result != nil || ran.Load() != 2 {
panic("expected both functions and no errors")
}
}

Collecting Concurrent Errors

If any of the functions passed to Go return an error, Wait collects these errors and returns a single, non-nil error value that contains all the errors that occurred. The order in which the functions execute or in which the errors are collected is not guaranteed.

When you call Wait, it is essential to check if the returned value is nil. A non-nil value indicates that at least one of your concurrent operations failed.

package main

import (
"errors"
"sync/atomic"

"github.com/hashicorp/go-multierror"
)

func main() {
var group multierror.Group
var ran atomic.Int32
group.Go(func() error { ran.Add(1); return errors.New("alpha") })
group.Go(func() error { ran.Add(1); return errors.New("beta") })
result := group.Wait()
if result == nil || ran.Load() != 2 {
panic("expected both functions and errors")
}
}