{"record":{"id":"91a5b64b8ce0424d","repo":"apache/druid","slug":"only-dimensionselectors-which-support-idlookup-a","errorCode":null,"errorMessage":"Only DimensionSelectors which support idLookup() are supported yet","messagePattern":"Only DimensionSelectors which support idLookup\\(\\) are supported yet","errorType":"exception","errorClass":"UnsupportedOperationException","httpStatus":null,"severity":"error","filePath":"processing/src/main/java/org/apache/druid/query/topn/BaseTopNAlgorithm.java","lineNumber":307,"sourceCode":"    public void ignoreFirstN(int n)\n    {\n      ignoreFirstN = n;\n    }\n\n    @Override\n    public void keepOnlyN(int n)\n    {\n      keepOnlyN = n;\n    }\n\n    @VisibleForTesting\n    public Pair<Integer, Integer> computeStartEnd(int cardinality)\n    {\n      int startIndex = ignoreFirstN;\n\n      if (previousStop != null) {\n        if (idLookup == null) {\n          throw new UnsupportedOperationException(\"Only DimensionSelectors which support idLookup() are supported yet\");\n        }\n        int lookupId = idLookup.lookupId(previousStop) + 1;\n        if (lookupId < 0) {\n          lookupId *= -1;\n        }\n        if (lookupId > ignoreFirstN + keepOnlyN) {\n          startIndex = ignoreFirstN + keepOnlyN;\n        } else {\n          startIndex = Math.max(lookupId, startIndex);\n        }\n      }\n\n      int endIndex = Math.min(ignoreFirstN + keepOnlyN, cardinality);\n\n      final TopNOptimizationInspector topNOptimizationInspector = cursorInspector.getOptimizationInspector();\n      if (ignoreAfterThreshold &&\n          query.getDimensionsFilter() == null &&\n          topNOptimizationInspector != null &&","sourceCodeStart":289,"sourceCodeEnd":325,"githubUrl":"https://github.com/apache/druid/blob/9b90983fd291f26935af934383ce360473179e4d/processing/src/main/java/org/apache/druid/query/topn/BaseTopNAlgorithm.java#L289-L325","documentation":"When resuming a topN scan after skipTo(previousStop), BaseTopNAlgorithm needs to translate the previous stop value into a dictionary id via the selector's idLookup(). If the DimensionSelector does not support idLookup (null), Druid throws UnsupportedOperationException because position-based resume is impossible.","triggerScenarios":"TopN scan with ignoreFirstN/keepOnlyN pagination hitting skipTo(previousStop) against a selector without idLookup support — e.g. expression/extraction dimensions or non-dictionary selectors combined with a non-null previousStop.","commonSituations":"TopN queries with time-order/pagination options on expression dimensions, cursor reuse across custom selectors lacking idLookup, dimensionSpecs (lookup/extraction) whose selectors don't implement lookup by id.","solutions":["Use a plain string dimension with dictionary encoding for topN queries","Remove the previousStop/time-order pagination options (run the query in one pass)","Pre-materialize lookup/extraction results as a real dimension at ingestion","Fall back to groupBy+limit which doesn't rely on idLookup"],"exampleFix":"// before\n{ \"dimension\": { \"type\": \"extraction\", \"dimension\": \"x\", \"extractionFn\": {\"type\":\"registeredLookup\"} } }\n// after\n{ \"dimension\": \"x_materialized\" }  // lookup applied at ingestion","handlingStrategy":"fallback","validationCode":"boolean safe = selector.supportsLookupNameFn() && previousStop == null || selector.idLookup() != null;","typeGuard":"static boolean supportsIdLookup(DimensionSelector s) { return s != null && s.idLookup() != null; }","tryCatchPattern":"try { return pagedTopNRun(query); }\ncatch (UnsupportedOperationException e) {\n  if (e.getMessage().contains(\"idLookup\")) { return singlePassTopNRun(query); }\n  throw e;\n}","preventionTips":["Avoid skipTo/pagination on extraction dimensionSpecs","Use plain dictionary-encoded dimensions for paged topN","Materialize lookups before querying"],"tags":["druid","topn","dimension-selector","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"}