{"record":{"id":"6de84dfff511ba07","repo":"elastic/elasticsearch","slug":"blocksize-must-be-a-power-of-2-in-got","errorCode":null,"errorMessage":"blockSize must be a power of 2 in [{}, {}], got: {}","messagePattern":"blockSize must be a power of 2 in \\[(.+?), (.+?)\\], got: (.+?)","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"libs/columnar/src/main/java/org/elasticsearch/columnar/ColumNARDocValuesFormat.java","lineNumber":64,"sourceCode":"    static final String DATA_CODEC = \"ColumNARNumericData\";\n    static final String DATA_EXTENSION = \"cnvd\";\n    static final String META_CODEC = \"ColumNARNumericMeta\";\n    static final String META_EXTENSION = \"cnvm\";\n\n    private final NumericPipelineSelector pipelineSelector;\n    private final int blockSize;\n\n    /**\n     * Constructs the format with a custom per-field pipeline selector and an explicit block size.\n     * The block size controls how many values are grouped into each encoded block; it must be a\n     * power of 2 between {@value #MIN_BLOCK_SIZE} and {@value #MAX_BLOCK_SIZE} inclusive.\n     *\n     * @throws IllegalArgumentException if {@code blockSize} is not a power of 2 in [{@value #MIN_BLOCK_SIZE}, {@value #MAX_BLOCK_SIZE}]\n     */\n    public ColumNARDocValuesFormat(NumericPipelineSelector pipelineSelector, int blockSize) {\n        super(ColumnarFormat.NAME);\n        if (blockSize < MIN_BLOCK_SIZE || blockSize > MAX_BLOCK_SIZE || (blockSize & (blockSize - 1)) != 0) {\n            throw new IllegalArgumentException(\n                \"blockSize must be a power of 2 in [\" + MIN_BLOCK_SIZE + \", \" + MAX_BLOCK_SIZE + \"], got: \" + blockSize\n            );\n        }\n        this.pipelineSelector = pipelineSelector;\n        this.blockSize = blockSize;\n    }\n\n    /** Constructs the format with a custom per-field pipeline selector and the default block size. */\n    public ColumNARDocValuesFormat(NumericPipelineSelector pipelineSelector) {\n        this(pipelineSelector, DEFAULT_BLOCK_SIZE);\n    }\n\n    /** SPI constructor. Uses the default pipeline for every field. */\n    public ColumNARDocValuesFormat() {\n        this((fieldName, type) -> NumericPipeline::defaultPipeline);\n    }\n\n    @Override","sourceCodeStart":46,"sourceCodeEnd":82,"githubUrl":"https://github.com/elastic/elasticsearch/blob/db6a809a667c081ca1dc7500389d26975573215f/libs/columnar/src/main/java/org/elasticsearch/columnar/ColumNARDocValuesFormat.java#L46-L82","documentation":"Thrown by the ColumNARDocValuesFormat constructor when blockSize fails any of three checks: below MIN_BLOCK_SIZE (128), above MAX_BLOCK_SIZE (8192), or not a power of two (the classic `n & (n-1) != 0` test). Block size bounds O(blockSize) per-field encoder allocations, and power-of-two is required by the bit-packing terminal. The SPI/no-arg constructor uses DEFAULT_BLOCK_SIZE (128); only explicit construction can hit this.","triggerScenarios":"Constructing `new ColumNARDocValuesFormat(selector, blockSize)` with values like 100 (below min), 9000 (above max), 1000 (not power of two), 256 (valid), or 8192 (valid). Also if a codec is instantiated reflectively/SPI from a config string parsed to an invalid int.","commonSituations":"A test tuning block size for benchmarking and guessing a value. Config plumbing that forwards a user int without validating the power-of-two constraint. Migrating a Lucene codecs param (where 1–512 ranges are common) without re-scaling to ColumNAR's 128–8192 range.","solutions":["Use one of the documented powers of two in [128, 8192]: 128, 256, 512, 1024, 2048, 4096, 8192.","Prefer the no-arg/SPI constructor or the single-arg constructor, which use DEFAULT_BLOCK_SIZE (128).","If accepting user input, validate: `Integer.bitCount(blockSize) == 1 && blockSize >= 128 && blockSize <= 8192` before constructing.","Round the requested size up to the next power of two and clamp into [128, 8192]."],"exampleFix":"// before\nnew ColumNARDocValuesFormat(selector, 1000); // throws\n\n// after\nnew ColumNARDocValuesFormat(selector, 1024); // valid power of two in range","handlingStrategy":"validation","validationCode":"void requireValidBlockSize(int blockSize) {\n    if (Integer.bitCount(blockSize) != 1\n        || blockSize < ColumNARDocValuesFormat.MIN_BLOCK_SIZE\n        || blockSize > ColumNARDocValuesFormat.MAX_BLOCK_SIZE) {\n        throw new IllegalArgumentException(\"blockSize must be a power of 2 in [128, 8192], got: \" + blockSize);\n    }\n}","typeGuard":"static boolean isValidBlockSize(int blockSize) {\n    return Integer.bitCount(blockSize) == 1\n        && blockSize >= ColumNARDocValuesFormat.MIN_BLOCK_SIZE\n        && blockSize <= ColumNARDocValuesFormat.MAX_BLOCK_SIZE;\n}","tryCatchPattern":"try {\n    return new ColumNARDocValuesFormat(selector, blockSize);\n} catch (IllegalArgumentException e) {\n    // fall back to the documented default rather than propagating a misconfiguration\n    return new ColumNARDocValuesFormat(selector); // uses DEFAULT_BLOCK_SIZE = 128","preventionTips":["Prefer the no-arg/SPI or single-arg constructor unless you have a measured reason to change block size.","When exposing block size as a config, validate power-of-two and range before construction.","Round requested sizes to the nearest valid power of two within [128, 8192].","Keep the validation helper close to the call site so it evolves with the constants."],"tags":["columnar","codec","lucene","validation","constructor","power-of-two"],"backgroundTag":null,"analyzedSha":"db6a809a667c081ca1dc7500389d26975573215f","analyzedAt":"2026-08-12T01:39:14.192Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}