{"record":{"id":"aa1ac7ba8d987a93","repo":"apache/druid","slug":"reverse-lookup-not-allowed","errorCode":null,"errorMessage":"Reverse lookup not allowed.","messagePattern":"Reverse lookup not allowed\\.","errorType":"exception","errorClass":"UnsupportedOperationException","httpStatus":null,"severity":"error","filePath":"extensions-contrib/spectator-histogram/src/main/java/org/apache/druid/spectator/histogram/SpectatorHistogramIndexed.java","lineNumber":141,"sourceCode":"  {\n    checkIndex(index);\n\n    NullableOffsetsHeader.Offset offset = offsetsHeader.get(index);\n    if (offset == null) {\n      return null;\n    }\n\n    ByteBuffer copyValueBuffer = valueBuffer.asReadOnlyBuffer();\n    copyValueBuffer.position(offset.getStart());\n    copyValueBuffer.limit(offset.getStart() + offset.getLength());\n\n    return strategy.fromByteBuffer(copyValueBuffer, offset.getLength());\n  }\n\n  @Override\n  public int indexOf(@Nullable SpectatorHistogram value)\n  {\n    throw new UnsupportedOperationException(\"Reverse lookup not allowed.\");\n  }\n\n  @Override\n  public Iterator<SpectatorHistogram> iterator()\n  {\n    return IndexedIterable.create(this).iterator();\n  }\n\n  @Override\n  public long getSerializedSize()\n  {\n    throw new UnsupportedOperationException(\"Serialization not supported here\");\n  }\n\n  @Override\n  public void writeTo(WritableByteChannel channel, SegmentFileBuilder fileBuilder)\n  {\n    throw new UnsupportedOperationException(\"Serialization not supported here\");","sourceCodeStart":123,"sourceCodeEnd":159,"githubUrl":"https://github.com/apache/druid/blob/9b90983fd291f26935af934383ce360473179e4d/extensions-contrib/spectator-histogram/src/main/java/org/apache/druid/spectator/histogram/SpectatorHistogramIndexed.java#L123-L159","documentation":"indexOf performs a reverse lookup (value -> ordinal), which is not implemented for SpectatorHistogramIndexed because SpectatorHistogram values have no cheap equality/ordering suitable for dictionary reversal. Any call throws UnsupportedOperationException.","triggerScenarios":"Calling indexOf(value) on the histogram column's Indexed object, typically via generic code paths that assume all Indexed implementations support reverse lookup (e.g. certain group-by or filter machinery).","commonSituations":"Plugging spectator-histogram columns into query paths that do dictionary lookups by value; custom code that iterates selectors and tries to map a histogram back to a dictionary id.","solutions":["Do not call indexOf on this column type; track dictionary ids alongside selectors instead of looking them up by value.","Refactor the calling code to compare values via the selector interface rather than ordinals.","If a reverse lookup is genuinely needed, use a different column type that supports it."],"exampleFix":"// before\nint ordinal = indexed.indexOf(value);\n// after\n// SpectatorHistogramIndexed cannot reverse-lookup; keep the ordinal from iteration instead.\nint ordinal = ordinalTrackedDuringIteration;","handlingStrategy":"type-guard","validationCode":"if (indexed instanceof SpectatorHistogramIndexed) { throw new UnsupportedOperationException(\"reverse lookup unsupported for this column type\"); }","typeGuard":"boolean supportsReverseLookup(Indexed<?> idx) { return !(idx instanceof SpectatorHistogramIndexed); }","tryCatchPattern":"try { return indexed.indexOf(value); } catch (UnsupportedOperationException e) { return -1; // fall back to iteration-based id tracking }","preventionTips":["Never assume every Druid Indexed supports indexOf; consult the column type.","Track ordinals at selection time rather than reverse-looking-up values.","Document SpectatorHistogramIndexed as iteration-only in internal helper APIs."],"tags":["java","druid","unsupported-operation"],"backgroundTag":"unsupported-operation","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"}