{"record":{"id":"4a034fcf183e3d21","repo":"apache/druid","slug":"maxvalue-s-must-be-positive","errorCode":null,"errorMessage":"maxValue[%s] must be positive","messagePattern":"maxValue\\[(.+?)\\] must be positive","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"processing/src/main/java/org/apache/druid/segment/data/VSizeColumnarInts.java","lineNumber":87,"sourceCode":"    for (int i = 0, size = ints.size(); i < size; i++) {\n      int val = ints.get(i);\n      if (val < 0) {\n        throw new IAE(\"integer values must be positive, got[%d], i[%d]\", val, i);\n      }\n      if (val > maxValue) {\n        throw new IAE(\"val[%d] > maxValue[%d], please don't lie about maxValue.  i[%d]\", val, maxValue, i);\n      }\n\n      helperBuffer.putInt(0, val);\n      buffer.put(helperBuffer.array(), Integer.BYTES - numBytes, numBytes);\n    }\n    buffer.position(0);\n  }\n\n  public static byte getNumBytesForMax(int maxValue)\n  {\n    if (maxValue < 0) {\n      throw new IAE(\"maxValue[%s] must be positive\", maxValue);\n    }\n\n    if (maxValue <= 0xFF) {\n      return 1;\n    } else if (maxValue <= 0xFFFF) {\n      return 2;\n    } else if (maxValue <= 0xFFFFFF) {\n      return 3;\n    }\n    return 4;\n  }\n\n  private final ByteBuffer buffer;\n  private final int numBytes;\n\n  private final int bitsToShift;\n  private final int size;\n","sourceCodeStart":69,"sourceCodeEnd":105,"githubUrl":"https://github.com/apache/druid/blob/9b90983fd291f26935af934383ce360473179e4d/processing/src/main/java/org/apache/druid/segment/data/VSizeColumnarInts.java#L69-L105","documentation":"VSizeColumnarInts.getNumBytesForMax computes how many bytes are needed to store values up to maxValue; a negative maxValue is meaningless for sizing, so it throws IAE immediately. Callers (e.g. numBytes) must pass a non-negative maxValue.","triggerScenarios":"Passing a negative maxValue to getNumBytesForMax, typically because maxValue was derived from data containing negatives or from an uninitialized/min-initialized accumulator (e.g. Integer.MIN_VALUE from Math.min over an empty set).","commonSituations":"Computing max with an initial value of Integer.MIN_VALUE when the dataset is empty, or reusing a minValue instead of maxValue variable.","solutions":["Fix the maxValue computation to initialize at 0 or guard against empty input","Clamp: pass Math.max(0, maxValue)","Validate the computed maxValue before calling getNumBytesForMax"],"exampleFix":"// before\nint maxValue = Integer.MIN_VALUE; for (int v : vals) maxValue = Math.min(maxValue, v); // wrong function\nbyte numBytes = VSizeColumnarInts.getNumBytesForMax(maxValue);\n// after\nint maxValue = 0; for (int v : vals) maxValue = Math.max(maxValue, v);\nbyte numBytes = VSizeColumnarInts.getNumBytesForMax(maxValue);","handlingStrategy":"validation","validationCode":"if (maxValue < 0) throw new IllegalArgumentException(\"maxValue must be non-negative, got \" + maxValue);\nbyte numBytes = VSizeColumnarInts.getNumBytesForMax(maxValue);","typeGuard":"boolean isValidMaxValue(long v) { return v >= 0 && v <= Integer.MAX_VALUE; }","tryCatchPattern":"try { numBytes = VSizeColumnarInts.getNumBytesForMax(maxValue); } catch (IAE e) { if (e.getMessage().contains(\"must be positive\")) { maxValue = Math.max(0, maxValue); numBytes = VSizeColumnarInts.getNumBytesForMax(maxValue); } else throw e; }","preventionTips":["Initialize max accumulators at 0, not Integer.MIN_VALUE","Guard against empty datasets before computing max","Name variables carefully to avoid passing minValue instead of maxValue"],"tags":["validation","integers","bounds"],"backgroundTag":"invalid-argument-value","analyzedSha":"9b90983fd291f26935af934383ce360473179e4d","analyzedAt":"2026-09-07T13:32:30.957Z","contentChangedAt":"2026-09-07T13:32:30.957Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}