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

  1. Check the segment file for corruption and reload the segment from a backup or re-ingest the data
  2. Verify the buffer position/byte order before constructing the reader — the header fields must be read in the writer's order
  3. Do not construct FrontCodedIndexed directly; use the standard read() factory which reads valid headers
  4. 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

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


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)