{"record":{"id":"0dbcee331967115d","repo":"apache/druid","slug":"can-only-handle-s-got-s-0dbcee","errorCode":null,"errorMessage":"Can only handle [%s], got [%s]","messagePattern":"Can only handle \\[(.+?)\\], got \\[(.+?)\\]","errorType":"exception","errorClass":"ISE","httpStatus":null,"severity":"error","filePath":"processing/src/main/java/org/apache/druid/query/topn/TopNQueryQueryToolChest.java","lineNumber":623,"sourceCode":"  {\n    private final QueryRunner<Result<TopNResultValue>> runner;\n\n    public ThresholdAdjustingQueryRunner(\n        QueryRunner<Result<TopNResultValue>> runner\n    )\n    {\n      this.runner = runner;\n    }\n\n    @Override\n    public Sequence<Result<TopNResultValue>> run(\n        QueryPlus<Result<TopNResultValue>> queryPlus,\n        ResponseContext responseContext\n    )\n    {\n      Query<Result<TopNResultValue>> input = queryPlus.getQuery();\n      if (!(input instanceof TopNQuery)) {\n        throw new ISE(\"Can only handle [%s], got [%s]\", TopNQuery.class, input.getClass());\n      }\n\n      final TopNQuery query = (TopNQuery) input;\n      final int minTopNThreshold = query.context()\n                                        .getInt(QueryContexts.MIN_TOP_N_THRESHOLD, TopNQueryConfig.DEFAULT_MIN_TOPN_THRESHOLD);\n      if (query.getThreshold() > minTopNThreshold) {\n        return runner.run(queryPlus, responseContext);\n      }\n\n      final boolean isBySegment = query.context().isBySegment();\n\n      return Sequences.map(\n          runner.run(queryPlus.withQuery(query.withThreshold(minTopNThreshold)), responseContext),\n          new Function<>()\n          {\n            @Override\n            public Result<TopNResultValue> apply(Result<TopNResultValue> input)\n            {","sourceCodeStart":605,"sourceCodeEnd":641,"githubUrl":"https://github.com/apache/druid/blob/9b90983fd291f26935af934383ce360473179e4d/processing/src/main/java/org/apache/druid/query/topn/TopNQueryQueryToolChest.java#L605-L641","documentation":"TopNQueryQueryToolChest only knows how to process TopNQuery objects. This guard in its query runner wiring throws IllegalStateException whenever a query of any other type is routed into the TopN tool chest, which is an internal dispatch invariant in Druid's query processing pipeline.","triggerScenarios":"Calling TopNQueryQueryToolChest.getQueryRunner (or the underlying runner) with a QueryPlus whose wrapped Query is not a TopNQuery — e.g. a misconfigured QueryRunnerFactoryRegistry maps a segment to the wrong tool chest, custom code passes a TimeseriesQuery/GroupByQuery/ScanQuery into the TopN tool chest directly, or a broker forwards a deserialized query whose type was corrupted.","commonSituations":"Custom extension code that builds a QueryRunner chain by hand and picks the wrong factory; bug in query-type dispatch after adding a new query type; tests that feed mock queries into a TopN tool chest.","solutions":["Ensure the Query passed to the TopN tool chest is constructed via TopNQueryBuilder / a TopNQueryDataSource","Check QueryRunnerFactory registration so each query class maps to its own tool chest (QueryRunnerFactoryConglomerate)","If writing custom dispatch code, instanceof-check the Query before invoking TopNQueryQueryToolChest","Inspect serialized query payloads between broker and historical for type corruption"],"exampleFix":"// before\nQueryRunner<Result<TopNResultValue>> runner = toolChest.getQueryRunner(seg, metric, new QueryPlus<>(genericQuery, null), ctx);\n// after\nif (genericQuery instanceof TopNQuery) {\n  QueryRunner<Result<TopNResultValue>> runner = toolChest.getQueryRunner(seg, metric, new QueryPlus<>((TopNQuery) genericQuery, null), ctx);\n} else {\n  throw new IAE(\"Expected TopNQuery, got %s\", genericQuery.getClass());\n}","handlingStrategy":"type-guard","validationCode":"if (!(query instanceof TopNQuery)) {\n  throw new IllegalArgumentException(\"TopN tool chest requires a TopNQuery, got \" + query.getClass());\n}","typeGuard":"static boolean isTopNQuery(Query<?> q) {\n  return q instanceof TopNQuery;\n}","tryCatchPattern":"try {\n  runner.run(queryPlus, responseContext).toList();\n} catch (IllegalStateException e) {\n  if (e.getMessage().startsWith(\"Can only handle\")) {\n    throw new IllegalArgumentException(\"Wrong query type routed to TopN tool chest\", e);\n  }\n  throw e;\n}","preventionTips":["Build queries with TopNQueryBuilder so the concrete type is always TopNQuery","Register each query class with its matching QueryRunnerFactory in the conglomerate","instanceof-check query type before handing it to any QueryToolChest"],"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"}