grpc/grpc-go · error
buffer size is not an exponent of two
Error message
buffer size is not an exponent of two
What it means
Returned by NewCircularBuffer when the requested size is not a power of two. The circular buffer uses a bitwise AND mask (size-1) instead of modulo for index wrapping, which requires the size to be 2^k. The check size&(size-1)!=0 catches non-powers-of-two. This is used internally by gRPC's profiling subsystem to store per-RPC stats.
Source
Thrown at internal/profiling/buffer/buffer.go:139
// Note that CircularBuffer is built for performance more than reliability.
// That is, some Push operations may fail without retries in some situations
// (such as during a Drain operation). Order of pushes is not maintained
// either; that is, if A was pushed before B, the Drain operation may return an
// array with B before A. These restrictions are acceptable within gRPC's
// profiling, but if your use-case does not permit these relaxed constraints
// or if performance is not a primary concern, you should probably use a
// lock-based data structure such as internal/buffer.UnboundedBuffer.
type CircularBuffer struct {
drainMutex sync.Mutex
qp []*queuePair
// qpn is a monotonically incrementing counter that's used to determine
// which queuePair a Push operation should write to. This approach's
// performance was found to be better than writing to a random queue.
qpn uint32
qpMask uint32
}
var errInvalidCircularBufferSize = errors.New("buffer size is not an exponent of two")
// NewCircularBuffer allocates a circular buffer of size size and returns a
// reference to the struct. Only circular buffers of size 2^k are allowed
// (saves us from having to do expensive modulo operations).
func NewCircularBuffer(size uint32) (*CircularBuffer, error) {
if size&(size-1) != 0 {
return nil, errInvalidCircularBufferSize
}
n := numCircularBufferPairs
if size/numCircularBufferPairs < 8 {
// If each circular buffer is going to hold less than a very small number
// of items (let's say 8), using multiple circular buffers is very likely
// wasteful. Instead, fallback to one circular buffer holding everything.
n = 1
}
cb := &CircularBuffer{View on GitHub (pinned to 0c51461d27)
Solutions
- Use a power-of-two buffer size: 1024, 2048, 4096, 8192, 16384, 32768, 65536, etc.
- If passing 0, note that InitStats defaults to 16384 (16<<10) when streamStatsSize is 0—use 0 to get the default rather than passing a bad value.
- Round your desired size up to the nearest power of two using bits.Len or a helper.
Example fix
// before
cb, err := buffer.NewCircularBuffer(1000) // 1000 is not 2^k
// after
cb, err := buffer.NewCircularBuffer(1024) // 2^10
// or round up to nearest power of two:
func nextPow2(n uint32) uint32 {
if n == 0 { return 1 }
p := uint32(1)
for p < n { p <<= 1 }
return p
} Defensive patterns
Strategy: validation
Validate before calling
// Validate buffer size is a power of two before creating the circular buffer.
func isValidBufferSize(size uint32) bool {
return size > 0 && size&(size-1) == 0
}
if !isValidBufferSize(size) {
// round up to nearest power of two
p := uint32(1)
for p < size { p <<= 1 }
size = p
}
cb, err := buffer.NewCircularBuffer(size) Try / catch
cb, err := buffer.NewCircularBuffer(size)
if err != nil {
if strings.Contains(err.Error(), "exponent of two") {
// round up and retry
p := uint32(1)
for p < size { p <<= 1 }
cb, err = buffer.NewCircularBuffer(p)
}
} Prevention
- Always use power-of-two sizes for the profiling buffer (1024, 2048, 4096, ...).
- Pass 0 to profiling.InitStats to get the default 16384 rather than a bad custom value.
- Add a helper to round arbitrary sizes up to the nearest power of two.
- Document the power-of-two requirement in profiling config.
When it happens
Trigger: Calling buffer.NewCircularBuffer (or profiling.InitStats with a non-power-of-2 streamStatsSize) with sizes like 1000, 100, 3, 0, or any value where size & (size-1) != 0. Zero also fails this check.
Common situations: Configuring profiling service with an arbitrary buffer size (e.g., ProfilingConfig.StreamStatsSize set to a round number like 1000); passing 0 (which also fails the power-of-two test); custom profiling setup with a user-chosen capacity.
Related errors
- profiling may be initialized at most once
- invalid method name: should start with /
- invalid method name: suffix /method is missing
- server-side auth info is not of type alts.AuthInfo
- message authentication failed
AI-assisted analysis of grpc/grpc-go@0c51461d27 (2026-08-11).
Data as JSON: /api/errors/dc20b4d4e6fd8558.
Report an issue: GitHub.