Why does writing to a built-in map from many goroutines crash, and how do you fix it?
This program launches 100 goroutines that each write one entry into a shared built-in map. It crashes at runtime instead of finishing cleanly.
func main() {
m := map[int]int{}
var wg sync.WaitGroup
for i := 0; i < 100; i++ {
wg.Add(1)
go func(i int) { defer wg.Done(); m[i] = i }(i)
}
wg.Wait()
}
Find and fix the bug.
Built-in maps are not safe for concurrent use. Concurrent writes trip the runtime's guard and abort with fatal error: concurrent map writes — even a concurrent read alongside a write can crash. Fix: guard every access with a sync.Mutex (or RWMutex), or use sync.Map for concurrent workloads.
- ✗Assuming a built-in map is safe for concurrent writes if the keys differ
- ✗Thinking it is a recoverable data race rather than a fatal runtime error
- ✗Believing pre-sizing the map removes the need for synchronization
- →When is
sync.Mappreferable to aMutex-guarded plain map? - →Why can even a concurrent read with a write crash, not just two writes?
Find the bug
func main() {
m := map[int]int{}
var wg sync.WaitGroup
for i := 0; i < 100; i++ {
wg.Add(1)
go func(i int) { defer wg.Done(); m[i] = i }(i)
}
wg.Wait()
}
Why it crashes
Go's built-in map is not meant for concurrent use — even when each goroutine writes its own key. Internally the runtime sets a "writing in progress" flag; if another goroutine begins a write (or read) at the same time, the runtime detects it and aborts the program:
fatal error: concurrent map writes
This is not a recoverable data race but an immediate fatal crash. Distinct keys do not help: the shared bucket structure is one object, and a parallel write corrupts it.
✅ Fixes:
// 1) guard with a mutex
var mu sync.Mutex
go func(i int){ defer wg.Done(); mu.Lock(); m[i] = i; mu.Unlock() }(i)
// 2) sync.Map for concurrent workloads
var m sync.Map
go func(i int){ defer wg.Done(); m.Store(i, i) }(i)