{"record":{"id":"dc20b4d4e6fd8558","repo":"grpc/grpc-go","slug":"buffer-size-is-not-an-exponent-of-two","errorCode":null,"errorMessage":"buffer size is not an exponent of two","messagePattern":"buffer size is not an exponent of two","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/profiling/buffer/buffer.go","lineNumber":139,"sourceCode":"// Note that CircularBuffer is built for performance more than reliability.\n// That is, some Push operations may fail without retries in some situations\n// (such as during a Drain operation). Order of pushes is not maintained\n// either; that is, if A was pushed before B, the Drain operation may return an\n// array with B before A. These restrictions are acceptable within gRPC's\n// profiling, but if your use-case does not permit these relaxed constraints\n// or if performance is not a primary concern, you should probably use a\n// lock-based data structure such as internal/buffer.UnboundedBuffer.\ntype CircularBuffer struct {\n\tdrainMutex sync.Mutex\n\tqp         []*queuePair\n\t// qpn is a monotonically incrementing counter that's used to determine\n\t// which queuePair a Push operation should write to. This approach's\n\t// performance was found to be better than writing to a random queue.\n\tqpn    uint32\n\tqpMask uint32\n}\n\nvar errInvalidCircularBufferSize = errors.New(\"buffer size is not an exponent of two\")\n\n// NewCircularBuffer allocates a circular buffer of size size and returns a\n// reference to the struct. Only circular buffers of size 2^k are allowed\n// (saves us from having to do expensive modulo operations).\nfunc NewCircularBuffer(size uint32) (*CircularBuffer, error) {\n\tif size&(size-1) != 0 {\n\t\treturn nil, errInvalidCircularBufferSize\n\t}\n\n\tn := numCircularBufferPairs\n\tif size/numCircularBufferPairs < 8 {\n\t\t// If each circular buffer is going to hold less than a very small number\n\t\t// of items (let's say 8), using multiple circular buffers is very likely\n\t\t// wasteful. Instead, fallback to one circular buffer holding everything.\n\t\tn = 1\n\t}\n\n\tcb := &CircularBuffer{","sourceCodeStart":121,"sourceCodeEnd":157,"githubUrl":"https://github.com/grpc/grpc-go/blob/0c51461d27177d997e14c642fe18c11668fc09a3/internal/profiling/buffer/buffer.go#L121-L157","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\ncb, err := buffer.NewCircularBuffer(1000) // 1000 is not 2^k\n// after\ncb, err := buffer.NewCircularBuffer(1024) // 2^10\n\n// or round up to nearest power of two:\nfunc nextPow2(n uint32) uint32 {\n    if n == 0 { return 1 }\n    p := uint32(1)\n    for p < n { p <<= 1 }\n    return p\n}","handlingStrategy":"validation","validationCode":"// Validate buffer size is a power of two before creating the circular buffer.\nfunc isValidBufferSize(size uint32) bool {\n    return size > 0 && size&(size-1) == 0\n}\nif !isValidBufferSize(size) {\n    // round up to nearest power of two\n    p := uint32(1)\n    for p < size { p <<= 1 }\n    size = p\n}\ncb, err := buffer.NewCircularBuffer(size)","typeGuard":null,"tryCatchPattern":"cb, err := buffer.NewCircularBuffer(size)\nif err != nil {\n    if strings.Contains(err.Error(), \"exponent of two\") {\n        // round up and retry\n        p := uint32(1)\n        for p < size { p <<= 1 }\n        cb, err = buffer.NewCircularBuffer(p)\n    }\n}","preventionTips":["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."],"tags":["profiling","buffer","validation","internal","grpc"],"backgroundTag":null,"analyzedSha":"0c51461d27177d997e14c642fe18c11668fc09a3","analyzedAt":"2026-08-11T14:49:15.055Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}