deadlock
Go Programming Language
Severity: CriticalWhat Does This Error Mean?
A deadlock in Go means every goroutine in your program is blocked and waiting — none can proceed. Go detects this situation and immediately panics with the message: 'all goroutines are asleep - deadlock!' This usually happens with channels or mutexes where goroutines are waiting for each other in a circle. Fix it by making sure every channel send has a matching receive, and that locks are always released.
Affected Models
- Go 1.0 and later
- All Go versions
- go build, go run, go test
Common Causes
- Sending to a channel with no receiver running — the sender waits forever
- Receiving from a channel that is never written to or never closed
- Two goroutines each waiting for the other to send first — a circular wait
- Locking a mutex and then trying to lock it again in the same goroutine without unlocking first
- Forgetting to close a channel that a range loop is waiting on
How to Fix It
-
Read the deadlock panic output carefully. It prints the stack trace for every goroutine and what each one is blocked on.
Look for lines mentioning 'chan receive', 'chan send', or 'semacquire'. These tell you what each goroutine is waiting for.
-
If you are sending to an unbuffered channel, make sure a goroutine is already running and waiting to receive before you send.
An unbuffered channel requires both sender and receiver to be ready at the same time. Start the receiver goroutine first.
-
If a goroutine ranges over a channel (for v := range ch), make sure the channel is closed when sending is done: close(ch)
Without close(ch), the range loop waits forever after the last value, causing a deadlock.
-
Check for mutex usage. Always call Unlock() after Lock(), preferably with defer: defer mu.Unlock()
Using defer mu.Unlock() right after mu.Lock() ensures the mutex is always released, even if the function returns early or panics.
-
If the deadlock only happens sometimes (not always), you likely have a race condition. Run tests with the -race flag: go test -race
The race detector finds concurrent access bugs that cause intermittent deadlocks and other hard-to-reproduce issues.
When to Call a Professional
A deadlock crashes the entire Go program. Go detects it and prints a full stack trace showing every blocked goroutine. Read the stack trace carefully — it shows what each goroutine is waiting for. Deadlocks in concurrent code can be tricky. If you are new to Go channels, start with simple patterns before adding complexity.
Frequently Asked Questions
What is a goroutine?
A goroutine is Go's version of a lightweight thread. You start one with the go keyword: go myFunction(). Goroutines run concurrently — multiple can run at the same time. Deadlocks happen when goroutines get stuck waiting for each other with no way to move forward.
What is a channel and why does it cause deadlocks?
A channel is a pipe that goroutines use to send and receive values to each other. An unbuffered channel requires both ends — one sending and one receiving — to be ready at the same time. If a goroutine tries to send but nobody is receiving, it waits forever. If all goroutines are waiting, it is a deadlock.
My deadlock only happens sometimes — is that still a deadlock?
If it only happens sometimes, it is more likely a race condition rather than a classic deadlock. A race condition is when two goroutines access shared data at the same time without proper synchronization. Use 'go test -race' to detect race conditions. They can eventually lead to a deadlock or corrupt data.