Skip to content
Reliability and safety

concurrentcapture

Checks locals mutated by goroutines launched repeatedly.

Fix offeredNo

What it detects

Goroutines launched from a loop should not mutate the same captured local without synchronization. Prefer returning results through a channel or keeping the value local to each goroutine.

Checks

Check What it detects
shared-capture
Reports repeatedly launched goroutines that mutate the same captured local.

Why this is flagged

Those goroutines run at unpredictable times and may read or write the captured value simultaneously. The result is a data race whose output can change from run to run and may only fail under load.

Further reading: Data race detector, The Go memory model.

How to fix it

Keep the changing value local to each goroutine and send the finished result back through a channel. If the value truly must be shared, protect every access with the same synchronization mechanism.

Examples

Flagged code

func collect(items []int) error {
var err error
for range items {
go func() {
// gohawk: captured local err is mutated by goroutines launched repeatedly
err = fetch()
}()
}
return err
}

Accepted code

func collectSafely(items []int) error {
var group sync.WaitGroup
errs := make(chan error, len(items))
for range items {
group.Add(1)
go func() {
defer group.Done()
errs <- fetch()
}()
}
group.Wait()
close(errs)
for err := range errs {
if err != nil {
return err
}
}
return nil
}