errgroup Patterns

Updated

September 8, 2026

errgroup Patterns

Overview

golang.org/x/sync/errgroup runs a set of goroutines and returns the first error, optionally canceling siblings via derived context. Prefer it over ad-hoc WaitGroup+mutex error slots for fan-out.

Diagram: cancel on first error

sequence (top → bottom):
  actors: errgroup, task A, task B
  errgroup --> task A  : Go
  errgroup --> task B  : Go
  task A --> errgroup  : err
  errgroup --> task B  : ctx cancel
  errgroup --> errgroup  : Wait returns err

Basic API

g, ctx := errgroup.WithContext(parent)
g.Go(func() error {
    return work(ctx)
})
g.Go(func() error {
    return other(ctx)
})
if err := g.Wait(); err != nil {
    return err
}

Limits

g.SetLimit(8) // max concurrent Gs in this group (modern x/sync)

Bounds fan-out without a hand-rolled semaphore.

Rules

  1. Pass ctx into every blocking call
  2. Return errors; do not log+ignore inside Go funcs without policy
  3. Wait once; do not reuse group
  4. First error wins—others may still run until cancel observed

vs WaitGroup

WaitGroup errgroup
Error prop manual built-in
Cancel siblings manual WithContext
Limit concurrency manual SetLimit

Experiment

go get golang.org/x/sync/errgroup
g, ctx := errgroup.WithContext(context.Background())
g.Go(func() error {
    <-ctx.Done()
    return ctx.Err()
})
g.Go(func() error {
    return errors.New("boom")
})
fmt.Println(g.Wait())

What to notice: First error cancels context; the other returns context.Canceled or exits promptly if it watches ctx.

Try next: Crawl N URLs with SetLimit(5) and a shared ctx timeout.