← Debugging Guides

阅读中文版本

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.

intermediatedebugginggoconcurrency

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.go

Interpretation

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 side

Interpretation

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