{"record":{"id":"6c08f3286e16b70c","repo":"apache/druid","slug":"windowoperatorquery-must-run-on-top-of-a-query-or","errorCode":null,"errorMessage":"WindowOperatorQuery must run on top of a query or inline data source, got [%s]","messagePattern":"WindowOperatorQuery must run on top of a query or inline data source, got \\[(.+?)\\]","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"processing/src/main/java/org/apache/druid/query/operator/WindowOperatorQuery.java","lineNumber":81,"sourceCode":"\n    // We can re-write scan-style sub queries into an operator instead of doing the actual Scan query.  So, we\n    // check for that and, if we are going to do the rewrite, then we return the sub datasource such that the\n    // parent constructor in BaseQuery stores the actual data source that we want to be distributed to.\n\n    // At this point, we could also reach into a QueryDataSource and validate that the ordering expected by the\n    // partitioning at least aligns with the ordering coming from the underlying query.  We unfortunately don't\n    // have enough information to validate that the underlying ordering aligns with expectations for the actual\n    // window operator queries, but maybe we could get that and validate it here too.\n    if (dataSource instanceof QueryDataSource) {\n      final Query<?> subQuery = ((QueryDataSource) dataSource).getQuery();\n      if (subQuery instanceof ScanQuery) {\n        return subQuery.getDataSource();\n      }\n      return dataSource;\n    } else if (dataSource instanceof InlineDataSource) {\n      return dataSource;\n    } else {\n      throw new IAE(\"WindowOperatorQuery must run on top of a query or inline data source, got [%s]\", dataSource);\n    }\n  }\n\n  private final RowSignature rowSignature;\n  private final List<OperatorFactory> operators;\n  private final List<OperatorFactory> leafOperators;\n\n  @JsonCreator\n  public WindowOperatorQuery(\n      @JsonProperty(\"dataSource\") DataSource dataSource,\n      @JsonProperty(\"intervals\") QuerySegmentSpec intervals,\n      @JsonProperty(\"context\") Map<String, Object> context,\n      @JsonProperty(\"outputSignature\") RowSignature rowSignature,\n      @JsonProperty(\"operatorDefinition\") List<OperatorFactory> operators,\n      @JsonProperty(\"leafOperators\") List<OperatorFactory> leafOperators\n  )\n  {\n    super(","sourceCodeStart":63,"sourceCodeEnd":99,"githubUrl":"https://github.com/apache/druid/blob/9b90983fd291f26935af934383ce360473179e4d/processing/src/main/java/org/apache/druid/query/operator/WindowOperatorQuery.java#L63-L99","documentation":"WindowOperatorQuery.validateMaybeRewriteDataSource requires the query's data source to be either a query data source (with a sub-query) or an InlineDataSource, since window operators run on materialized rows. Any other data source type (e.g. a bare table datasource) is rejected with this IAE.","triggerScenarios":"Constructing a WindowOperatorQuery (or running a window-function native query) whose DataSource is directly a table/lookup/etc. rather than a query or inline source.","commonSituations":"Hand-built native window queries pointing at a table datasource without an intermediate scan/query, or tooling that rewrites SQL window queries to native but fails to insert the base query layer.","solutions":["Wrap the data source in a QueryDataSource (e.g. a ScanQuery/TableScan) so the window operator reads from a sub-query","Use an InlineDataSource for small pre-materialized inputs","Fix the query rewrite/translation layer that constructs WindowOperatorQuery to always emit query or inline sources"],"exampleFix":"// before\nnew WindowOperatorQuery(tableDataSource, ...)\n// after\nnew WindowOperatorQuery(\n  new QueryDataSource(newScanQueryBuilder().dataSource(tableDataSource).build()), ...)","handlingStrategy":"validation","validationCode":"DataSource ds = query.getDataSource();\nif (!(ds instanceof QueryDataSource) && !(ds instanceof InlineDataSource)) {\n  throw new IllegalArgumentException(\"WindowOperatorQuery needs query/inline source, got \" + ds.getClass());\n}","typeGuard":"boolean isValidWindowSource(DataSource ds) {\n  return ds instanceof QueryDataSource || ds instanceof InlineDataSource;\n}","tryCatchPattern":"try {\n  windowOperatorQuery.validateMaybeRewriteDataSource(dataSource);\n} catch (IAE e) {\n  if (e.getMessage().startsWith(\"WindowOperatorQuery must run on top of\")) {\n    dataSource = new QueryDataSource(scanQueryOver(dataSource));\n  } else throw e;\n}","preventionTips":["Always wrap base datasources (table etc.) in a QueryDataSource when building window operator queries","Use InlineDataSource only for small, already-materialized inputs","Add unit tests for native window query builders to assert the data source type before construction"],"tags":["window-operator","datasource","validation"],"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"}