Skip to content
Latchkey

Go "fatal error: concurrent map writes" in Tests - Fix in CI

Go maps are not safe for concurrent use. When two goroutines write the same map (or one writes while another reads) without synchronization, the runtime detects it and aborts the whole process with a fatal error.

What this error means

A test run crashes hard with fatal error: concurrent map writes (or concurrent map read and map write) and a goroutine dump - not a normal --- FAIL. It can be intermittent, surfacing only when goroutines happen to collide.

go test output
fatal error: concurrent map writes

goroutine 34 [running]:
	main.(*Cache).Set(...)
	/app/cache.go:21 +0x1f4

Common causes

A shared map written from multiple goroutines

A cache, registry, or result map is mutated concurrently without a lock. The runtime’s built-in check aborts the process on the unsafe access.

Tests exposing a real production data race

Parallel tests (t.Parallel()) or concurrent helpers exercise a map that production code never protected, surfacing a genuine race.

How to fix it

Guard the map with a mutex

Serialize access with a sync.Mutex/RWMutex around every read and write.

Go
type Cache struct {
	mu sync.RWMutex
	m  map[string]string
}
func (c *Cache) Set(k, v string) {
	c.mu.Lock(); defer c.mu.Unlock()
	c.m[k] = v
}

Use sync.Map for concurrent access

For read-heavy concurrent maps, sync.Map avoids manual locking.

Go
var m sync.Map
m.Store(key, value)
v, ok := m.Load(key)

Catch it with the race detector

Run with -race to get the exact conflicting accesses, then add synchronization.

Terminal
go test -race ./...

How to prevent it

  • Protect shared maps with a mutex or use sync.Map.
  • Run go test -race in CI to catch unsynchronized access early.
  • Treat any fatal map error as a real production bug, not a flake.

Related guides

Tired of flaky CI? Latchkey auto-heals failed jobs and retries them for you. Start free →