Check if a Go Channel Buffer Is Full or Empty (len, cap, select)

Use len and cap on buffered Go channels to detect full or empty buffers, try a send with select and default, and know why unbuffered channels and “is it closed?” need different patterns.

Published

Updated

Read time 4 min read

Reviewed byDeepak Prasad

Check if a Go Channel Buffer Is Full or Empty (len, cap, select)

If you already know channels in Go and buffered channels, this page focuses on a narrow question people search for: how to tell if a buffered channel’s queue is full or empty, or how to try a send without blocking. It also covers why unbuffered channels behave differently and why “check closed then write” is not something the language encourages.

Searches such as golang check if channel is full, golang check if channel is empty, golang channel length, or golang channel size all map to the same primitives: len, cap, and a select with default. For closed channels and sends, see the FAQ and the short section below—there is no race-free isClosed() helper in the standard story.

Tested with Go 1.26.4 linux/amd64 on Linux kernel 6.14.0-37-generic.


Buffered channels: len, cap, full, and empty

For ch := make(chan T, n) with n > 0, the channel has a buffer of size n. The language spec defines:

  • len(ch) — how many elements are queued in the buffer right now.
  • cap(ch) — the buffer size n (the maximum len can reach before a send would block).

Empty buffer: len(ch) == 0 (nothing waiting to be received).

Full buffer: len(ch) == cap(ch) (no free slot; another send would block unless something receives first).

Those expressions are snapshots. Another goroutine can send or receive immediately after you read len, so use them when you already control concurrency or when a rough hint is enough—not as a substitute for a proper handoff protocol.

go
package main

import "fmt"

func main() {
	ch := make(chan int, 2)
	fmt.Println("empty?", len(ch) == 0, "len", len(ch), "cap", cap(ch))

	ch <- 1
	fmt.Println("full?", len(ch) == cap(ch), "len", len(ch))

	ch <- 2
	fmt.Println("full?", len(ch) == cap(ch), "len", len(ch), "cap", cap(ch))

	<-ch
	fmt.Println("after one recv, full?", len(ch) == cap(ch), "len", len(ch))
}
Output

If you use Run on that snippet, the printed lines walk through empty vs full as values are sent and one is received—len reaches cap when both buffer slots hold values.


Try a send (or receive) without blocking: select and default

To detect “would this send block because the buffer is full?” without actually blocking, use select with a default branch. If the send cannot proceed immediately, control falls through to default.

go
package main

import "fmt"

func main() {
	ch := make(chan int, 2)
	ch <- 1
	ch <- 2

	select {
	case ch <- 3:
		fmt.Println("sent 3")
	default:
		fmt.Println("send would block — buffer is full (for this moment)")
	}
}
Output

With Run, the program takes the default branch and prints that the send would block because the buffer is full.

The same idea works for a non-blocking receive: select { case v := <-ch: ... default: ... }.


Unbuffered channels

For make(chan T) with no capacity argument, cap(ch) is 0 and len(ch) is 0 at all times. You cannot use len/cap to mean “full” or “empty” here—there is no buffer to measure. Use select with default if you need a non-blocking handshake, or redesign so a dedicated goroutine owns the channel end.


Closed channels, receives, and sends

To see whether a receive still delivers real data, use the comma-ok form:

go
v, ok := <-ch
if !ok {
	// channel closed and drained (no more real values)
}

There is no standard, race-free way to answer “is this channel closed?” right before a send. Sending on a closed channel panics. If you see searches like golang check if channel is closed before writing, the safe patterns are: one goroutine closes, others only send until they are told to stop (via another channel, context.Context, or shared state under a mutex)—not a pre-send isClosed() check.


Summary

On a buffered channel, golang channel length is len(ch) and golang channel size (buffer capacity) is cap(ch). Full means len(ch) == cap(ch); empty means len(ch) == 0. To react without blocking, use select with default. Unbuffered channels always report len 0 and cap 0, so those tests do not apply. Closed channels are observed from the receive side with v, ok := <-ch; avoid trying to “check closed” immediately before a send. For background on concurrency, see goroutines and golang concurrency.


References


Frequently Asked Questions

1. How do I check if a Go channel buffer is full?

For a buffered channel, compare len(ch) == cap(ch). That means every slot in the queue currently holds a value. It only applies while no other goroutine is simultaneously sending or receiving on the same channel.

2. How do I check if a Go channel is empty?

For a buffered channel, len(ch) == 0 means no values are waiting in the buffer. Receiving with a two-value form v, ok := <-ch tells you whether a receive delivered a real value or a zero value because the channel was closed and drained.

3. Can I check if a channel is closed before sending?

There is no safe built-in “isClosed” API. Sending on a closed channel panics. Use a separate done channel, context cancellation, or another synchronization pattern instead of probing closed state right before a send.

4. Do len and cap work on unbuffered channels?

Both are zero for an unbuffered channel, so len and cap do not tell you whether a peer is waiting. Use select with default to try a non-blocking send or receive, or structure the program so you do not need that probe.
Deepak Prasad

R&D Engineer

Founder of GoLinuxCloud with more than 15 years of expertise in Linux, Python, Go, Laravel, DevOps, Kubernetes, Git, Shell scripting, OpenShift, AWS, Networking, and Security. With extensive …