Golang / GoLang Production Patterns and Web Standards Interview Questions
How do you correctly propagate errors from concurrent goroutines in a Go service?
When multiple goroutines run in parallel, errors must be collected and propagated without data races or goroutine leaks. The idiomatic tools are errgroup for structured concurrency and buffered channels for ad-hoc patterns.
import "golang.org/x/sync/errgroup" // Pattern 1: errgroup â run N tasks, fail fast on first error func fetchUserProfile(ctx context.Context, userID int) (*Profile, error) { g, ctx := errgroup.WithContext(ctx) // gctx cancelled when any goroutine fails or g.Wait() returns var user *User g.Go(func() error { var err error user, err = userRepo.FindByID(ctx, userID) return fmt.Errorf("fetching user: %w", err) }) var orders []Order g.Go(func() error { var err error orders, err = orderRepo.ListByUser(ctx, userID) return fmt.Errorf("fetching orders: %w", err) }) if err := g.Wait(); err != nil { return nil, err // first error from any goroutine } return buildProfile(user, orders), nil } // Pattern 2: collect ALL errors (don't fail fast) func validateItems(ctx context.Context, items []Item) error { errs := make([]error, len(items)) var wg sync.WaitGroup wg.Add(len(items)) for i, item := range items { go func(idx int, it Item) { defer wg.Done() errs[idx] = validateItem(ctx, it) // each goroutine writes its own slot }(i, item) } wg.Wait() // Filter nil errors and join var nonNil []error for _, e := range errs { if e != nil { nonNil = append(nonNil, e) } } return errors.Join(nonNil...) } // errgroup with concurrency limit (Go 1.20+) g.SetLimit(10) // max 10 goroutines running at once for _, item := range items { item := item g.Go(func() error { return process(ctx, item) }) }
More Related questions...