Go "all goroutines are asleep - deadlock!" in Tests - Fix in CI
The Go runtime detected that every goroutine is blocked with no way to make progress and aborts with a deadlock fatal error. A test is waiting on a channel, WaitGroup, or lock that nothing will ever release.
What this error means
A test crashes with fatal error: all goroutines are asleep - deadlock! and a goroutine dump showing where each is blocked. It is deterministic when the logic is wrong, and the dump points at the blocking operation.
fatal error: all goroutines are asleep - deadlock!
goroutine 1 [chan receive]:
main_test.go:18 +0x65Common causes
A receive on a channel no one sends to
A test reads from a channel that is never written (or a sender exited early), so the receive blocks forever.
A WaitGroup or mutex misuse
A WaitGroup with more Wait than Done, or a lock acquired twice without release, leaves goroutines permanently blocked.
How to fix it
Ensure every channel has a sender and a close
Guarantee a send for each receive, and close channels when production is done so ranges terminate.
done := make(chan struct{})
go func() { defer close(done); work() }()
<-done // unblocks when the goroutine finishesBalance WaitGroup Add/Done
Call Add before launching goroutines and exactly one Done per goroutine.
var wg sync.WaitGroup
for _, t := range tasks {
wg.Add(1)
go func(t Task) { defer wg.Done(); run(t) }(t)
}
wg.Wait()Bound the wait with a timeout
Use a select with a timeout so a stuck test fails with a clear message instead of deadlocking.
select {
case v := <-ch:
_ = v
case <-time.After(2 * time.Second):
t.Fatal("timed out waiting for result")
}How to prevent it
- Match every channel receive with a guaranteed send (and close).
- Keep WaitGroup
Add/Donebalanced. - Use
selectwith a timeout in tests that wait on concurrency.