apache/druid · error · IllegalStateException
Got a [ ] which isn't a
Error message
Got a [%s] which isn't a %s
What it means
SearchQueryRunner.run() requires the query inside QueryPlus to be a SearchQuery; anything else throws ISE('Got a [%s] which isn't a %s'). This is an internal type invariant at the segment runner level, mirroring the toolchest check at SearchQueryQueryToolChest.run(). It indicates the search runner received a misrouted query object.
Solutions
- Route only SearchQuery instances to SearchQueryRunner
- Correct custom QueryToolChest/warehouse wiring so query types map to their proper runners
- Unwrap or convert the query to SearchQuery before invoking the runner
Example fix
// before
searchRunner.run(QueryPlus.wrap(otherQuery), responseContext);
// after
if (otherQuery instanceof SearchQuery) {
searchRunner.run(QueryPlus.wrap((Query<Result<SearchResultValue>>) otherQuery), responseContext);
} Defensive patterns
Strategy: type-guard
Validate before calling
if (!(query instanceof SearchQuery)) { throw new IllegalArgumentException("SearchQueryRunner 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 searchRunner.run(queryPlus, ctx); } catch (ISE e) { if (e.getMessage().startsWith("Got a [")) { fixQueryRouting(); } else throw e; } Prevention
- Bind SearchQueryRunner only to SearchQuery in runner factories
- Avoid reflection-based query construction that erases concrete types
- Add assertion tests verifying query type before runner dispatch
When it happens
Trigger: Custom runner chains passing a non-SearchQuery to SearchQueryRunner; toolchest mapping that binds SearchQueryRunner to the wrong query class; programmatically wrapping queries incorrectly.
Common situations: Custom extension runner stacks; reflection-based query construction losing type; test harnesses assembling QueryPlus manually with the wrong query.
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
- Can only handle [ ], got [ ]
- Unknown format [ ]
- attempt to get boolean[] null vector from string[] only…
- attempt to get double[] from string[] only scalar binding
- attempt to get long[] from string[] only scalar binding
AI-assisted analysis of apache/druid@9b90983fd2 (2026-09-07).
Data as JSON: /api/errors/a2234f991475c06c.
Report an issue: GitHub.
Appendix: source
Thrown at processing/src/main/java/org/apache/druid/query/search/SearchQueryRunner.java:218
{
if (selector != null) {
final String dimVal = selector.isNull() ? null : String.valueOf(selector.getDouble());
if (searchQuerySpec.accept(dimVal)) {
set.addTo(new SearchHit(outputName, dimVal), 1);
}
}
}
}
@Override
public Sequence<Result<SearchResultValue>> run(
final QueryPlus<Result<SearchResultValue>> queryPlus,
ResponseContext responseContext
)
{
Query<Result<SearchResultValue>> input = queryPlus.getQuery();
if (!(input instanceof SearchQuery)) {
throw new ISE("Got a [%s] which isn't a %s", input.getClass(), SearchQuery.class);
}
final SearchQuery query = (SearchQuery) input;
final List<SearchQueryExecutor> plan = strategySelector.strategize(query).getExecutionPlan(query, segment);
final Object2IntRBTreeMap<SearchHit> retVal = new Object2IntRBTreeMap<>(query.getSort().getComparator());
retVal.defaultReturnValue(0);
int remain = query.getLimit();
for (final SearchQueryExecutor executor : plan) {
retVal.putAll(executor.execute(remain));
remain -= retVal.size();
}
return makeReturnResult(segment, query.getLimit(), retVal);
}
private static Sequence<Result<SearchResultValue>> makeReturnResult(
Segment segment,View on GitHub (pinned to 9b90983fd2)