A common mistake is pairing sync.WaitGroup with an unfinished error strategy. Shared error slices, mutexes, or error channels add coordination work; errgroup.WithContext combines synchronization, first-error propagation, and context cancellation. section 9.13
func handler(ctx context.Context, circles []Circle) ([]Result, error) {
results := make([]Result, len(circles))
g, ctx := errgroup.WithContext(ctx) #1
for i, circle := range circles {
i := i
circle := circle
g.Go(func() error { #2
result, err := foo(ctx, circle)
if err != nil {
return err
}
results[i] = result
return nil
})
}
if err := g.Wait(); err != nil { #3
return nil, err
}
return results, nil
}
Use errgroup.WithContext to derive a context, start one g.Go call per circle, return errors from each closure, and retrieve the first non-nil error with g.Wait. section 9.13
Pass the derived context to each operation when a fast failure should make remaining work cancelable. Return results only after g.Wait succeeds; otherwise return the error immediately. section 9.13
