Simpler error handling in goroutines

August 29, 2025

Join me later today at 3:00pm EDT (19:00 UTC), for more live coding. Subscribe on YouTube and hit the Notify me button so you don’t miss it!


Yesterday we looked at how to handle errors from multiple goroutines using an error channel. While functional, the approach is somewhat complicated, and easy to get wrong.

Enter errgroup. While not part of the standard library, it is maintained by the Go team, and is widely used in the Go ecosystem. It provides a simple way to manage a group of goroutines that may return errors.

Let’s first look at our example code rewritten to use errgroup, then we’ll discuss.

func doManyThings() error {
	var g errgroup.Group

	for i := range 100 {
		g.Go(func() error {
			return doSomething(i)
		})
	}

	return g.Wait()
}

Whoa!

How much shorter and simpler that is, compared to the previous version! From 26 lines down to 11, with no loss in functionality.

So let’s see what this new code is doing.

First, we define a new errgroup.Group object. The zero value of an errgroup.Group is ready to use, so we don’t need to do any special initialization.

Then we call g.Go() with a function literal for each of our 100 tasks. This is similar to how we used sync.WaitGroup, but instead of calling wg.Go()*, we call g.Go(). The function we pass to g.Go() must return an error, which errgroup will capture.

Finally, we call g.Wait(), which blocks until all goroutines have completed. If any of the goroutines returned a non-nil error, g.Wait() will return that error. If multiple goroutines returned errors, only the first one is returned.

*Historical note: errgroup’s Go() method predates, and inspired the addition of Go() to sync.WaitGroup in Go 1.25.


Share this

Direct to your inbox, daily. I respect your privacy .

Unsure? Browse the archive .

Get daily content like this in your inbox!

Subscribe