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

  1. Use a power-of-two buffer size: 1024, 2048, 4096, 8192, 16384, 32768, 65536, etc.
  2. 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.
  3. 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

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


AI-assisted analysis of grpc/grpc-go@0c51461d27 (2026-08-11). Data as JSON: /api/errors/dc20b4d4e6fd8558. Report an issue: GitHub.