Concurrency · 3 MIN READ
errgroup: errors, cancellation and a real concurrency limit
Coordinate fallible parallel work without confusing cancellation with joining.
golang.org/x/sync/errgroup is a versioned external module, not part of sync. WithContext supplies a derived cancellation context and Wait returns a task error. Tasks must still cooperate with cancellation, and a limit on active tasks is different from a limit on queued input bytes or waiting requests.
errgroup: errors, cancellation and a real concurrency limit · A MAP OF THE IDEA
- Choose a task budget
- Propagate group context
- Collect a task error
- Join before publishing
Useful when
- Coordinate fallible parallel work without confusing cancellation with joining.
- Choose one owner for submission, cancellation, joining and result publication. An error-aware group simplifies coordination but does not supply a durable transaction or every admission policy.
Watch out
- SetLimit bounds active group functions, not every queued byte. All started work must be joined before its dependencies are released.
Implementation & operation cost: Choose the policy from the actual contract, then verify its failure path in the target environment.
Use the derived context for the owned operation
A group cancels its derived context when a task returns a non-nil error or when Wait returns. Keep that context inside the operation. Even a successful Wait leaves it canceled, so later independent work must use the caller’s appropriate context instead.
group, workCtx := errgroup.WithContext(ctx)
group.SetLimit(4)
for i, id := range ids {
if workCtx.Err() != nil {
break
}
group.Go(func() error {
value, err := load(workCtx, id)
if err != nil {
return err
}
output[i] = value
return nil
})
}
if err := group.Wait(); err != nil {
return nil, err
}
if err := ctx.Err(); err != nil {
return nil, err
}
return output, nilThis native function-body fragment uses the errgroup package, modern loop declarations, a preallocated output slice and a context-aware load function. The simple submission loop assumes load always exits on cancellation. The group does not deep-copy returned values.
Understand where SetLimit blocks
Go waits for room when the group is at its configured limit; that admission wait is not itself a ctx-select operation. Do not change the limit while tasks are active. TryGo can express nonblocking admission when the caller has a rejection policy. A fixed worker pool may fit a streaming or explicitly bounded queue better.
Join after a submission failure
Production submission may fail before every task is added. Do not return and close dependencies while already started tasks still use them. Cancel the operation and call Wait on every exit path that has launched work. Returning from a helper is not a join; a surrounding owner must enforce that lifecycle.
Separate partial results from successful results
A failed group may have populated some result slots before cancellation. Decide whether the operation permits partial success and represent it explicitly. For all-or-nothing results, return no successful result after Wait fails. Database transactions and idempotency still decide which external effects survive.
TAKE THIS WITH YOU
Choose one owner for submission, cancellation, joining and result publication. An error-aware group simplifies coordination but does not supply a durable transaction or every admission policy.
Check your understanding
A successful group.Wait returns. Which context should an independent later operation use?
Go to the source
Progress stays in this browser. No account needed.