Avoid reinventing goroutine coordination when several parallel calls must return one error. sync.WaitGroup can wait for completion, but it leaves error collection, synchronization, and cancellation to additional shared state or channels. section 9.13
errgroup lets each goroutine return an error directly. With errgroup.WithContext, the group also supplies a derived context; g.Wait reports an error when any call fails, while successful calls store results by index. section 9.13 section 9.15
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
}
Trace the corrected handler: create the group and context, launch one g.Go call per input, return errors from workers, then check g.Wait before returning results. section 9.13
Choose the coordination approach according to the requirement:
- Use sync.WaitGroup with shared result storage when completion tracking is the only concern and error handling can be designed separately. section 9.13
- Use errgroup when parallel work needs direct error propagation together with synchronization and context cancellation. section 9.15

