{"record":{"id":"d2f9bf1da5f4a13a","repo":"apache/druid","slug":"got-a-s-which-isn-t-a-s-d2f9bf","errorCode":null,"errorMessage":"Got a [%s] which isn't a %s","messagePattern":"Got a \\[(.+?)\\] which isn't a (.+?)","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"processing/src/main/java/org/apache/druid/query/timeseries/TimeseriesQueryRunnerFactory.java","lineNumber":109,"sourceCode":"        TimeseriesQueryEngine engine,\n        CursorFactory cursorFactory,\n        @Nullable TimeBoundaryInspector timeBoundaryInspector\n    )\n    {\n      this.engine = engine;\n      this.cursorFactory = cursorFactory;\n      this.timeBoundaryInspector = timeBoundaryInspector;\n    }\n\n    @Override\n    public Sequence<Result<TimeseriesResultValue>> run(\n        QueryPlus<Result<TimeseriesResultValue>> queryPlus,\n        ResponseContext responseContext\n    )\n    {\n      Query<Result<TimeseriesResultValue>> input = queryPlus.getQuery();\n      if (!(input instanceof TimeseriesQuery)) {\n        throw new ISE(\"Got a [%s] which isn't a %s\", input.getClass(), TimeseriesQuery.class);\n      }\n\n      return engine.process(\n          (TimeseriesQuery) input,\n          cursorFactory,\n          timeBoundaryInspector,\n          (TimeseriesQueryMetrics) queryPlus.getQueryMetrics()\n      );\n    }\n  }\n}\n","sourceCodeStart":91,"sourceCodeEnd":121,"githubUrl":"https://github.com/apache/druid/blob/9b90983fd291f26935af934383ce360473179e4d/processing/src/main/java/org/apache/druid/query/timeseries/TimeseriesQueryRunnerFactory.java#L91-L121","documentation":"TimeseriesQueryRunnerFactory's runner only accepts TimeseriesQuery instances. run() type-checks the incoming QueryPlus payload and throws ISE if some other query class reaches the timeseries runner — an internal dispatch/wiring bug.","triggerScenarios":"A QueryPlus carrying a non-TimeseriesQuery (e.g. TopNQuery, GroupByQuery) is routed to the timeseries query runner factory, typically due to custom QueryToolChest/wiring, incorrect query type registration, or broken deserialization of query type fields.","commonSituations":"Custom extensions registering query runners with the wrong type mapping, forwarding queries between clusters with modified 'query/type' fields, classpath mixing Druid versions where query types changed package/name.","solutions":["Verify the query's JSON \"query\" type is \"timeseries\" and not altered in transit","Check extension versions: all Druid components must run the same version (no mixed classpath)","Ensure QueryRunnerFactory registrations map the correct query class to TimeseriesQueryRunnerFactory","Capture the full failing query JSON and validate it deserializes to TimeseriesQuery"],"exampleFix":"// before\nQueryRunner<Row> r = factoryMap.get(\"topn\").createRunner(segment); // wired wrong\n// after\nQueryRunner<Result<TimeseriesResultValue>> r = factoryMap.get(\"timeseries\").createRunner(segment);","handlingStrategy":"type-guard","validationCode":"if (!(queryPlus.getQuery() instanceof TimeseriesQuery)) { throw new IllegalArgumentException(\"wrong runner for query type \" + queryPlus.getQuery().getClass()); }","typeGuard":"static boolean isTimeseries(Query<?> q) { return q instanceof TimeseriesQuery; }","tryCatchPattern":"try { return runner.run(queryPlus, ctx); }\ncatch (IllegalStateException e) { if (e.getMessage().startsWith(\"Got a [\")) { throw new QueryInterruptedException(e); } throw e; }","preventionTips":["Keep all Druid components on identical versions","Register factories with correct query class mappings","Never rewrite the 'type' field of query JSON in transit"],"tags":["druid","internal-dispatch","timeseries","type-mismatch"],"backgroundTag":"type-mismatch","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"}