apache/druid · error · ISE
Got a [ ] which isn't a
Error message
Got a [%s] which isn't a %s
What it means
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.
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
Example fix
// before QueryPlus<Result<TopNResultValue>> input = new QueryPlus<>(groupByExample, null); QueryRunner<Result<TopNResultValue>> r = factory.createRunner(segment); r.run(input, new HashMap<>()); // after TopNQuery topNExample = new TopNQueryBuilder().dataSource(ds).intervals(q).dimension(dim).metric(m).threshold(10).aggregators(aggs).build(); QueryPlus<Result<TopNResultValue>> input = new QueryPlus<>(topNExample, null); QueryRunner<Result<TopNResultValue>> r = factory.createRunner(segment); r.run(input, new HashMap<>());
Defensive patterns
Strategy: type-guard
Validate before calling
if (!(queryPlus.getQuery() instanceof TopNQuery)) {
throw new IllegalArgumentException("TopN runner requires TopNQuery, got " + queryPlus.getQuery().getClass());
} Type guard
static boolean isTopNQuery(QueryPlus<?> input) {
return input.getQuery() instanceof TopNQuery;
} Try / catch
try {
segmentRunner.run(queryPlus, responseContext).toList();
} catch (IllegalStateException e) {
if (e.getMessage().contains("which isn't a")) {
// reroute via QueryRunnerFactoryConglomerate using the query's actual class
}
throw e;
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.
Related errors
- Can only handle [ ], got [ ]
- AggregatorFactoryNotMergeableException
- Expected expression with just one binding
- Expected expression with just one binding
- Number of segment descriptors does not equal number of…
AI-assisted analysis of apache/druid@9b90983fd2 (2026-09-07).
Data as JSON: /api/errors/97f15876efd2b605.
Report an issue: GitHub.
Appendix: source
Thrown at processing/src/main/java/org/apache/druid/query/topn/TopNQueryRunnerFactory.java:74
this.computationBufferPool = computationBufferPool;
this.toolchest = toolchest;
this.queryWatcher = queryWatcher;
}
@Override
public QueryRunner<Result<TopNResultValue>> createRunner(final Segment segment)
{
final TopNQueryEngine queryEngine = new TopNQueryEngine(computationBufferPool);
return new QueryRunner<>()
{
@Override
public Sequence<Result<TopNResultValue>> run(
QueryPlus<Result<TopNResultValue>> input,
ResponseContext responseContext
)
{
if (!(input.getQuery() instanceof TopNQuery)) {
throw new ISE("Got a [%s] which isn't a %s", input.getClass(), TopNQuery.class);
}
TopNQuery query = (TopNQuery) input.getQuery();
return queryEngine.query(
query,
segment,
(TopNQueryMetrics) input.getQueryMetrics()
);
}
};
}
@Override
public QueryRunner<Result<TopNResultValue>> mergeRunners(
QueryProcessingPool queryProcessingPool,
Iterable<QueryRunner<Result<TopNResultValue>>> queryRunners
)View on GitHub (pinned to 9b90983fd2)