apache/druid · error · IllegalStateException

Can only handle [ ], got [ ]

Error message

Can only handle [%s], got [%s]

What it means

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.

Solutions

  1. Ensure the Query passed in QueryPlus is an instance of SearchQuery
  2. Fix custom toolchest/runner registration so each query class maps to its own toolchest
  3. If wrapping queries, unwrap to the original SearchQuery before invoking run()

Example fix

// before
QueryPlus<Result<SearchResultValue>> qp = QueryPlus.wrap(someTimeseriesQuery);
searchToolChest.run(qp, responseContext);
// after
if (someQuery instanceof SearchQuery) {
  searchToolChest.run(QueryPlus.wrap((Query<Result<SearchResultValue>>) someQuery), responseContext);
}
Defensive patterns

Strategy: type-guard

Validate before calling

if (!(query instanceof SearchQuery)) { throw new IllegalArgumentException("search toolchest requires SearchQuery, got " + query.getClass()); }

Type guard

Optional<SearchQuery> asSearchQuery(Query<?> q) { return q instanceof SearchQuery ? Optional.of((SearchQuery) q) : Optional.empty(); }

Try / catch

try { return toolChest.run(queryPlus, ctx); } catch (ISE e) { if (e.getMessage().startsWith("Can only handle")) { rerouteToCorrectToolchest(); } else throw e; }

Prevention

When it happens

Trigger: 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.

Common situations: Extension code building custom query runners; testing harnesses that construct QueryPlus with the wrong query class; broker routing misconfiguration.

Understand the failure class

Background: "is not a compatible type" / "cannot merge" errors: when a value's type doesn't match what the library requires — this error's family across 65 libraries.

Related errors


AI-assisted analysis of apache/druid@9b90983fd2 (2026-09-07). Data as JSON: /api/errors/33b0286fbf26ebd1. Report an issue: GitHub.

Appendix: source

Thrown at processing/src/main/java/org/apache/druid/query/search/SearchQueryQueryToolChest.java:331

    public SearchThresholdAdjustingQueryRunner(
        QueryRunner<Result<SearchResultValue>> runner,
        SearchQueryConfig config
    )
    {
      this.runner = runner;
      this.config = config;
    }

    @Override
    public Sequence<Result<SearchResultValue>> run(
        QueryPlus<Result<SearchResultValue>> queryPlus,
        ResponseContext responseContext
    )
    {
      Query<Result<SearchResultValue>> input = queryPlus.getQuery();
      if (!(input instanceof SearchQuery)) {
        throw new ISE("Can only handle [%s], got [%s]", SearchQuery.class, input.getClass());
      }

      final SearchQuery query = (SearchQuery) input;
      if (query.getLimit() < config.getMaxSearchLimit()) {
        return runner.run(queryPlus, responseContext);
      }

      final boolean isBySegment = query.context().isBySegment();

      return Sequences.map(
          runner.run(queryPlus.withQuery(query.withLimit(config.getMaxSearchLimit())), responseContext),
          new Function<>()
          {
            @Override
            public Result<SearchResultValue> apply(Result<SearchResultValue> input)
            {
              if (isBySegment) {
                BySegmentSearchResultValue value = (BySegmentSearchResultValue) input.getValue();

View on GitHub (pinned to 9b90983fd2)