Go — fatal error: all goroutines are asleep on an unbuffered channel
Resolve channel deadlocks by matching sends and receives and adding buffering or select with default.
Symptoms
- fatal error: all goroutines are asleep - deadlock!
- A send blocks because no receiver is ready
- The program hangs when the worker returns early
- A receive waits forever after senders finished
Possible causes
- An unbuffered channel send has no active receiver
- A goroutine returns before receiving while another blocks on send
- A range over a channel never sees close so the loop blocks
- Too few receivers for the number of senders
Diagnosis steps
Find sends and receives for the channel
grep -n "ch <-\|<-ch\|range ch\|close(ch)" main.goInterpretation
Listing all operations shows whether each send has a matching receive and whether the channel is closed.
Add buffering or a default branch
ch := make(chan int, 1) ; select { case ch <- x: default: }Interpretation
Buffering or a select default prevents a send from blocking when no receiver is ready.
Close only from the sender
close(ch) // producer sideInterpretation
Closing tells receivers no more values will arrive, letting range terminate; closing from the receiver can panic.
Root cause
A goroutine blocked forever on a channel operation because no counterparty ever performed the matching operation, so the runtime detected that all goroutines were asleep.
Fix
Ensure every unbuffered send has a ready receiver, buffer where sends can outrun receives, close channels from the sender side so range terminates, and use select with a default or timeout to avoid indefinite blocking.
Verify the fix
Run the program and confirm it completes without the deadlock message, and confirm all expected values are sent and received.
Symptom walkthrough
The runtime terminates the program with fatal error: all goroutines are asleep - deadlock when every goroutine is blocked on a channel and none can proceed. This happens when an unbuffered send has no receiver ready, when a goroutine exits before receiving, or when a range waits on a channel that is never closed. The message is precise: no progress is possible, so the program cannot continue.
Fix in practice
Make each send have a matching receive, add buffering where producers can legitimately run ahead, and close channels from the sender so receivers exit their range. Use select with a default or timeout when blocking indefinitely is unacceptable, which also protects against a missing counterparty.
Verification
Run the program and confirm it completes without the deadlock message, then confirm all expected values were both sent and received. Completing with the right data confirms the channel protocol is balanced.
Diagnostic commands
Check the behavior with commands like these:
grep -n "ch <-\|<-ch\|range ch\|close(ch)" main.go
ch := make(chan int, 1) ; select { case ch <- x: default: }
close(ch) // producer side