{"record":{"id":"97f15876efd2b605","repo":"apache/druid","slug":"got-a-s-which-isn-t-a-s-97f158","errorCode":null,"errorMessage":"Got a [%s] which isn't a %s","messagePattern":"Got a \\[(.+?)\\] which isn't a (.+?)","errorType":"exception","errorClass":"ISE","httpStatus":null,"severity":"error","filePath":"processing/src/main/java/org/apache/druid/query/topn/TopNQueryRunnerFactory.java","lineNumber":74,"sourceCode":"    this.computationBufferPool = computationBufferPool;\n    this.toolchest = toolchest;\n    this.queryWatcher = queryWatcher;\n  }\n\n  @Override\n  public QueryRunner<Result<TopNResultValue>> createRunner(final Segment segment)\n  {\n    final TopNQueryEngine queryEngine = new TopNQueryEngine(computationBufferPool);\n    return new QueryRunner<>()\n    {\n      @Override\n      public Sequence<Result<TopNResultValue>> run(\n          QueryPlus<Result<TopNResultValue>> input,\n          ResponseContext responseContext\n      )\n      {\n        if (!(input.getQuery() instanceof TopNQuery)) {\n          throw new ISE(\"Got a [%s] which isn't a %s\", input.getClass(), TopNQuery.class);\n        }\n\n        TopNQuery query = (TopNQuery) input.getQuery();\n        return queryEngine.query(\n            query,\n            segment,\n            (TopNQueryMetrics) input.getQueryMetrics()\n        );\n      }\n    };\n\n  }\n\n  @Override\n  public QueryRunner<Result<TopNResultValue>> mergeRunners(\n      QueryProcessingPool queryProcessingPool,\n      Iterable<QueryRunner<Result<TopNResultValue>>> queryRunners\n  )","sourceCodeStart":56,"sourceCodeEnd":92,"githubUrl":"https://github.com/apache/druid/blob/9b90983fd291f26935af934383ce360473179e4d/processing/src/main/java/org/apache/druid/query/topn/TopNQueryRunnerFactory.java#L56-L92","documentation":"TopNQueryRunnerFactory's per-segment run() asserts that the incoming query is a TopNQuery before delegating to the TopN query engine. It throws IllegalStateException when any other query type reaches the TopN runner, indicating the query was routed to the wrong runner factory.","triggerScenarios":"Invoking TopNQueryRunnerFactory.SegmentTopNQueryRunner.run() with a QueryPlus holding a non-TopN query; calling it directly in unit tests (as seen in callers like testMultipleRunsThrowException, testCancel*) with a wrong-typed or mock QueryPlus; misrouted queries from a broker/historical where the conglomerate resolved to the TopN factory.","commonSituations":"Hand-written test harnesses constructing QueryPlus objects with the wrong query type; custom QueryRunner chains that bypass QueryRunnerFactoryConglomerate type dispatch; serialization/deserialization producing a base Query implementation.","solutions":["Pass a TopNQuery (built with TopNQueryBuilder) into the runner","In tests, build the QueryPlus with new QueryPlus<>(topNQuery, ...) using a real or mocked TopNQuery","Fix factory registration so TopNQueryRunnerFactory only receives TopNQuery instances","instanceof-check query type before constructing runner chains"],"exampleFix":"// before\nQueryPlus<Result<TopNResultValue>> input = new QueryPlus<>(groupByExample, null);\nQueryRunner<Result<TopNResultValue>> r = factory.createRunner(segment);\nr.run(input, new HashMap<>());\n// after\nTopNQuery topNExample = new TopNQueryBuilder().dataSource(ds).intervals(q).dimension(dim).metric(m).threshold(10).aggregators(aggs).build();\nQueryPlus<Result<TopNResultValue>> input = new QueryPlus<>(topNExample, null);\nQueryRunner<Result<TopNResultValue>> r = factory.createRunner(segment);\nr.run(input, new HashMap<>());","handlingStrategy":"type-guard","validationCode":"if (!(queryPlus.getQuery() instanceof TopNQuery)) {\n  throw new IllegalArgumentException(\"TopN runner requires TopNQuery, got \" + queryPlus.getQuery().getClass());\n}","typeGuard":"static boolean isTopNQuery(QueryPlus<?> input) {\n  return input.getQuery() instanceof TopNQuery;\n}","tryCatchPattern":"try {\n  segmentRunner.run(queryPlus, responseContext).toList();\n} catch (IllegalStateException e) {\n  if (e.getMessage().contains(\"which isn't a\")) {\n    // reroute via QueryRunnerFactoryConglomerate using the query's actual class\n  }\n  throw e;\n}","preventionTips":["In tests, construct QueryPlus with a real TopNQuery (TopNQueryBuilder), not mocks of generic Query","Route queries through QueryRunnerFactoryConglomerate.getQueryRunnerForQuery instead of instantiating runners directly","Keep query serialization symmetric between broker and historical so the concrete type survives"],"tags":["druid","query-processing","internal-invariant"],"backgroundTag":"internal-invariant-violation","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"}