{"record":{"id":"9386361031a4378e","repo":"apache/druid","slug":"index-d-size-d","errorCode":null,"errorMessage":"Index[%d] >= size[%d]","messagePattern":"Index\\[(.+?)\\] >= size\\[(.+?)\\]","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"extensions-contrib/spectator-histogram/src/main/java/org/apache/druid/spectator/histogram/SpectatorHistogramIndexed.java","lineNumber":105,"sourceCode":"    // The rest of the buffer is the values\n    valueBuffer = buffer.slice();\n  }\n\n  /**\n   * Checks  if {@code index} a valid `element index` in SpectatorHistogramIndexed.\n   * Similar to Preconditions.checkElementIndex() except this method throws {@link IAE} with custom error message.\n   * <p>\n   * Used here to get existing behavior(same error message and exception) of V1 GenericIndexed.\n   *\n   * @param index index identifying an element of an SpectatorHistogramIndexed.\n   */\n  private void checkIndex(int index)\n  {\n    if (index < 0) {\n      throw new IAE(\"Index[%s] < 0\", index);\n    }\n    if (index >= size) {\n      throw new IAE(\"Index[%d] >= size[%d]\", index, size);\n    }\n  }\n\n  public Class<? extends SpectatorHistogram> getClazz()\n  {\n    return strategy.getClazz();\n  }\n\n  @Override\n  public int size()\n  {\n    return size;\n  }\n\n  @Nullable\n  @Override\n  public SpectatorHistogram get(int index)\n  {","sourceCodeStart":87,"sourceCodeEnd":123,"githubUrl":"https://github.com/apache/druid/blob/9b90983fd291f26935af934383ce360473179e4d/extensions-contrib/spectator-histogram/src/main/java/org/apache/druid/spectator/histogram/SpectatorHistogramIndexed.java#L87-L123","documentation":"SpectatorHistogramIndexed is a dense indexed collection over SpectatorHistogram column values; checkIndex validates that a requested ordinal lies within [0, size). When get() is called with an index at or beyond the number of stored values, the IAE 'Index[%d] >= size[%d]' is thrown to prevent reads of uninitialized offsets into the underlying buffer.","triggerScenarios":"Calling SpectatorHistogramIndexed.get(index) with a negative-derived or stale index, e.g. iterating with a row count from a different segment version, or a caller that computed an index from an out-of-date dictionary size.","commonSituations":"Segment replacement during query execution making a cached Indexed reference stale; hand-written code scanning rows with a hardcoded or mis-computed bound; dictionary lookups against a column whose size shrunk after compaction.","solutions":["Re-fetch the Indexed instance from the current column holder and use its size() as the loop bound.","Verify the index source: ensure the caller uses size() from the same Indexed object, not a cached or computed count.","Check for concurrent segment swapping; re-run the query if a segment was replaced mid-scan.","If debugging, log the failing index and size and compare against the column's numRows metadata."],"exampleFix":"// before\nfor (int i = 0; i < cachedSize; i++) { histogram = indexed.get(i); }\n// after\nfor (int i = 0; i < indexed.size(); i++) { histogram = indexed.get(i); }","handlingStrategy":"validation","validationCode":"if (index < 0 || index >= indexed.size()) { throw new IllegalArgumentException(\"index \" + index + \" out of range 0..\" + (indexed.size() - 1)); }\nreturn indexed.get(index);","typeGuard":null,"tryCatchPattern":"try { return indexed.get(i); } catch (IAE e) { log.warn(\"stale index %d: %s\", i, e.getMessage()); return null; }","preventionTips":["Always bound loops with the same Indexed object's size(), never a cached count.","Re-acquire Indexed references after segment swaps instead of reusing old ones.","Write a unit test that iterates every index 0..size()-1 before deploying index math changes."],"tags":["java","druid","index-out-of-range"],"backgroundTag":"index-out-of-bounds","analyzedSha":"9b90983fd291f26935af934383ce360473179e4d","analyzedAt":"2026-09-07T13:32:30.957Z","contentChangedAt":"2026-09-07T13:32:30.957Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}