apache/druid · error · IllegalArgumentException

val[ ] > maxValue[ ], please don't lie about maxValue. i[ ]

Error message

val[%d] > maxValue[%d], please don't lie about maxValue.  i[%d]

What it means

writeToBuffer checks each value against the maxValue declared when the column was created; a value exceeding maxValue would overflow the chosen byte width, corrupting the packed encoding, so it throws this IAE ('don't lie about maxValue'). The declared maxValue must be a true upper bound of all values.

Solutions

  1. Compute maxValue by scanning all values before serialization (use a second pass or the fromIterable factory that computes it)
  2. Increase maxValue to the true maximum and use a byte width that fits it (getNumBytesForMax)
  3. If the true max can exceed 0xFFFFFFFF constraints, switch to VSizeLongSerde / long-based columns

Example fix

// before: maxValue from partial scan
int maxValue = sampledMax;
VSizeColumnarInts.fromIndexedInts(values, maxValue);
// after: compute true max
int maxValue = 0; for (int i = 0; i < values.size(); i++) maxValue = Math.max(maxValue, values.get(i));
VSizeColumnarInts.fromIndexedInts(values, maxValue);
Defensive patterns

Strategy: validation

Validate before calling

int trueMax = 0; for (int i = 0; i < ints.size(); i++) trueMax = Math.max(trueMax, ints.get(i));
if (trueMax > maxValue) throw new IllegalArgumentException("declared maxValue " + maxValue + " < actual " + trueMax);

Type guard

boolean fitsMaxValue(IndexedInts ints, int maxValue) { for (int i = 0; i < ints.size(); i++) { if (ints.get(i) > maxValue) return false; } return true; }

Try / catch

try { col = VSizeColumnarInts.fromIndexedInts(ints, maxValue); } catch (IAE e) { if (e.getMessage().contains("lie about maxValue")) { maxValue = recomputeMax(ints); col = VSizeColumnarInts.fromIndexedInts(ints, maxValue); } else throw e; }

Prevention

When it happens

Trigger: Calling VSizeColumnarInts.fromIndexedInts with a maxValue smaller than the actual maximum in the IndexedInts — e.g. maxValue computed over a sample rather than the full dataset.

Common situations: Two-pass dictionary encoding where maxValue came from the first pass, streaming ingestion where later rows exceed earlier max, or off-by-one errors when computing maxValue.

Understand the failure class

Background: "value must be between 0 and 1" / "out of range" / "must not be negative" errors: fixing range-validation failures across open-source libraries — this error's family across 42 libraries.

Related errors


AI-assisted analysis of apache/druid@9b90983fd2 (2026-09-07). Data as JSON: /api/errors/aaf8481e29f6aeba. Report an issue: GitHub.

Appendix: source

Thrown at processing/src/main/java/org/apache/druid/segment/data/VSizeColumnarInts.java:75

  {
    int numBytes = getNumBytesForMax(maxValue);

    final ByteBuffer buffer = ByteBuffer.allocate((ints.size() * numBytes) + (4 - numBytes));
    writeToBuffer(buffer, ints, numBytes, maxValue);

    return new VSizeColumnarInts(buffer.asReadOnlyBuffer(), numBytes);
  }

  private static void writeToBuffer(ByteBuffer buffer, IndexedInts ints, int numBytes, int maxValue)
  {
    ByteBuffer helperBuffer = ByteBuffer.allocate(Integer.BYTES);
    for (int i = 0, size = ints.size(); i < size; i++) {
      int val = ints.get(i);
      if (val < 0) {
        throw new IAE("integer values must be positive, got[%d], i[%d]", val, i);
      }
      if (val > maxValue) {
        throw new IAE("val[%d] > maxValue[%d], please don't lie about maxValue.  i[%d]", val, maxValue, i);
      }

      helperBuffer.putInt(0, val);
      buffer.put(helperBuffer.array(), Integer.BYTES - numBytes, numBytes);
    }
    buffer.position(0);
  }

  public static byte getNumBytesForMax(int maxValue)
  {
    if (maxValue < 0) {
      throw new IAE("maxValue[%s] must be positive", maxValue);
    }

    if (maxValue <= 0xFF) {
      return 1;
    } else if (maxValue <= 0xFFFF) {
      return 2;

View on GitHub (pinned to 9b90983fd2)