apache/druid · error · IllegalArgumentException

integer values must be positive, got

Error message

integer values must be positive, got[%d], i[%d]

What it means

VSizeColumnarInts.writeToBuffer validates every value before packing it into a variable-width buffer; negative integers cannot be encoded, so any val < 0 throws this IAE naming the offending value and its index. It is a data-validation guard during column serialization (via fromIndexedInts).

Solutions

  1. Sanitize input data so values are non-negative before building the column (clamp or map sentinels to 0/null)
  2. Fix the upstream producer (aggregator/expression) that emits negative values
  3. If negatives are legitimate, use a column type that supports signed values instead of VSizeColumnarInts

Example fix

// before
VSizeColumnarInts.fromIndexedInts(rawInts, maxValue);
// after
for (int i = 0; i < rawInts.size(); i++) { if (rawInts.get(i) < 0) throw new IllegalArgumentException("negative at " + i); }
VSizeColumnarInts.fromIndexedInts(rawInts, maxValue);
Defensive patterns

Strategy: validation

Validate before calling

for (int i = 0, size = ints.size(); i < size; i++) { if (ints.get(i) < 0) throw new IllegalArgumentException("negative value at index " + i); }

Type guard

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

Try / catch

try { col = VSizeColumnarInts.fromIndexedInts(ints, maxValue); } catch (IAE e) { if (e.getMessage().contains("must be positive")) { log.error("negative value in column data"); } throw e; }

Prevention

When it happens

Trigger: Calling VSizeColumnarInts.fromIndexedInts (or dimension serialization) with an IndexedInts containing a negative int, e.g. from a dictionary lookup returning -1 or a broken aggregator.

Common situations: Ingestion of numeric dimension values where a sentinel -1 or missing-value marker was not translated, custom aggregators producing negative dictionary ids, or integer underflow in upstream transforms.

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/30f3f7d1bd783970. Report an issue: GitHub.

Appendix: source

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

  }

  public static VSizeColumnarInts fromIndexedInts(IndexedInts ints, int maxValue)
  {
    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) {

View on GitHub (pinned to 9b90983fd2)