apache/druid · error · IllegalStateException
bucketSize must be a power of two but was[%,d]
Error message
bucketSize must be a power of two but was[%,d]
What it means
The FrontCodedIndexed reader constructor requires bucketSize to be a power of two (exactly one bit set) because the implementation uses bit tricks and bucket-relative offset math that only works for power-of-two buckets. A non-power-of-two value read from the header indicates corruption or a foreign format, so it throws ISE.
Solutions
- Check the segment file for corruption and reload the segment from a backup or re-ingest the data
- Verify the buffer position/byte order before constructing the reader — the header fields must be read in the writer's order
- Do not construct FrontCodedIndexed directly; use the standard read() factory which reads valid headers
- If your writer code produces the bucketSize, ensure it was validated as a power of two at write time (FrontCodedIndexedWriter enforces this)
Example fix
// before new FrontCodedIndexed<>(buffer, order, 6, numValues, hasNull, offsetsPosition); // ISE // after int bucketSize = 4; // power of two, matching what the writer used new FrontCodedIndexed<>(buffer, order, bucketSize, numValues, hasNull, offsetsPosition);
Defensive patterns
Strategy: validation
Validate before calling
if (bucketSize <= 0 || Integer.bitCount(bucketSize) != 1) {
throw new IllegalArgumentException("bucketSize must be a power of two: " + bucketSize);
} Try / catch
try {
FrontCodedIndexed<String> idx = FrontCodedIndexed.read(buffer, order);
} catch (IllegalStateException e) {
// corrupted header; reload or re-ingest the segment
} Prevention
- Always deserialize with the standard read() factory rather than direct construction
- Verify buffer position and ByteOrder before parsing headers
- Validate power-of-two bucketSize at write time (the writer already does)
- Re-ingest from source if a segment shows corruption
When it happens
Trigger: Reading a FrontCodedIndexed whose serialized header contains a bucketSize with more than one bit set (e.g. 3, 5, 6) — caused by a corrupted segment buffer, reading at the wrong offset, or byte-order mismatch shifting fields.
Common situations: Corrupted or hand-edited segment files; reading a buffer from the wrong position so unrelated bytes are interpreted as bucketSize; writers from incompatible versions; custom code constructing FrontCodedIndexed directly with a bad bucketSize.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- bucketSize must be a power of two but was[%,d]
- bucketSize must be a power of two (from 1 up to 128) but…
- integer values must be positive, got
- Unexpected null byte
- val[ ] > maxValue[ ], please don't lie about maxValue. i[ ]
AI-assisted analysis of apache/druid@9b90983fd2 (2026-09-07).
Data as JSON: /api/errors/7345d11fbed0ae24.
Report an issue: GitHub.
Appendix: source
Thrown at processing/src/main/java/org/apache/druid/segment/data/FrontCodedIndexed.java:154
protected final int numBuckets;
protected final int div;
protected final int rem;
protected final int offsetsPosition;
protected final int bucketsPosition;
protected final boolean hasNull;
protected final int lastBucketNumValues;
private FrontCodedIndexed(
ByteBuffer buffer,
ByteOrder order,
int bucketSize,
int numValues,
boolean hasNull,
int offsetsPosition
)
{
if (Integer.bitCount(bucketSize) != 1) {
throw new ISE("bucketSize must be a power of two but was[%,d]", bucketSize);
}
this.buffer = buffer.asReadOnlyBuffer().order(order);
this.bucketSize = bucketSize;
this.hasNull = hasNull;
this.numBuckets = (int) Math.ceil((double) numValues / (double) bucketSize);
this.adjustIndex = hasNull ? 1 : 0;
this.adjustedNumValues = numValues + adjustIndex;
this.div = Integer.numberOfTrailingZeros(bucketSize);
this.rem = bucketSize - 1;
this.lastBucketNumValues = (numValues & rem) == 0 ? bucketSize : numValues & rem;
this.offsetsPosition = offsetsPosition;
this.bucketsPosition = offsetsPosition + ((numBuckets - 1) * Integer.BYTES);
}
/**
* Get a value from a bucket at a relative position.
* <p>View on GitHub (pinned to 9b90983fd2)