{"record":{"id":"33b0286fbf26ebd1","repo":"apache/druid","slug":"can-only-handle-s-got-s","errorCode":null,"errorMessage":"Can only handle [%s], got [%s]","messagePattern":"Can only handle \\[(.+?)\\], got \\[(.+?)\\]","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"processing/src/main/java/org/apache/druid/query/search/SearchQueryQueryToolChest.java","lineNumber":331,"sourceCode":"\n    public SearchThresholdAdjustingQueryRunner(\n        QueryRunner<Result<SearchResultValue>> runner,\n        SearchQueryConfig config\n    )\n    {\n      this.runner = runner;\n      this.config = config;\n    }\n\n    @Override\n    public Sequence<Result<SearchResultValue>> run(\n        QueryPlus<Result<SearchResultValue>> queryPlus,\n        ResponseContext responseContext\n    )\n    {\n      Query<Result<SearchResultValue>> input = queryPlus.getQuery();\n      if (!(input instanceof SearchQuery)) {\n        throw new ISE(\"Can only handle [%s], got [%s]\", SearchQuery.class, input.getClass());\n      }\n\n      final SearchQuery query = (SearchQuery) input;\n      if (query.getLimit() < config.getMaxSearchLimit()) {\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.withLimit(config.getMaxSearchLimit())), responseContext),\n          new Function<>()\n          {\n            @Override\n            public Result<SearchResultValue> apply(Result<SearchResultValue> input)\n            {\n              if (isBySegment) {\n                BySegmentSearchResultValue value = (BySegmentSearchResultValue) input.getValue();","sourceCodeStart":313,"sourceCodeEnd":349,"githubUrl":"https://github.com/apache/druid/blob/9b90983fd291f26935af934383ce360473179e4d/processing/src/main/java/org/apache/druid/query/search/SearchQueryQueryToolChest.java#L313-L349","documentation":"SearchQueryQueryToolChest.run() only processes queries whose underlying Query is a SearchQuery; any other query type routed here throws ISE('Can only handle [%s], got [%s]'). This is an internal dispatch invariant — the toolchest is keyed to the SearchQuery class. Hitting it means a query of a different type was handed to the search toolchest, typically through misconfigured query wiring or custom code.","triggerScenarios":"Custom QueryRunner/toolchest wiring that routes non-SearchQuery objects into the search toolchest; reflection- or JSON-built queries losing their concrete type; calling run() programmatically with a wrapped or foreign Query.","commonSituations":"Extension code building custom query runners; testing harnesses that construct QueryPlus with the wrong query class; broker routing misconfiguration.","solutions":["Ensure the Query passed in QueryPlus is an instance of SearchQuery","Fix custom toolchest/runner registration so each query class maps to its own toolchest","If wrapping queries, unwrap to the original SearchQuery before invoking run()"],"exampleFix":"// before\nQueryPlus<Result<SearchResultValue>> qp = QueryPlus.wrap(someTimeseriesQuery);\nsearchToolChest.run(qp, responseContext);\n// after\nif (someQuery instanceof SearchQuery) {\n  searchToolChest.run(QueryPlus.wrap((Query<Result<SearchResultValue>>) someQuery), responseContext);\n}","handlingStrategy":"type-guard","validationCode":"if (!(query instanceof SearchQuery)) { throw new IllegalArgumentException(\"search toolchest requires SearchQuery, got \" + query.getClass()); }","typeGuard":"Optional<SearchQuery> asSearchQuery(Query<?> q) { return q instanceof SearchQuery ? Optional.of((SearchQuery) q) : Optional.empty(); }","tryCatchPattern":"try { return toolChest.run(queryPlus, ctx); } catch (ISE e) { if (e.getMessage().startsWith(\"Can only handle\")) { rerouteToCorrectToolchest(); } else throw e; }","preventionTips":["Register each Query class with its matching QueryToolChest in custom wiring","Never feed wrapped/foreign queries directly into the search toolchest","Write unit tests that assert runner chains route by query class"],"tags":["search-query","query-runner","type-mismatch","dispatch"],"backgroundTag":"incompatible-source-type","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"}