{"record":{"id":"2a1ba06718dd38db","repo":"apache/druid","slug":"subtotal-spec-s-is-either-not-a-subset-of-top-lev","errorCode":null,"errorMessage":"Subtotal spec %s is either not a subset of top level dimensions.","messagePattern":"Subtotal spec (.+?) is either not a subset of top level dimensions\\.","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"processing/src/main/java/org/apache/druid/query/groupby/GroupByQuery.java","lineNumber":269,"sourceCode":"  @Nullable\n  private List<List<String>> verifySubtotalsSpec(\n      @Nullable List<List<String>> subtotalsSpec,\n      List<DimensionSpec> dimensions\n  )\n  {\n    // if subtotalsSpec exists then validate that all are subsets of dimensions spec.\n    if (subtotalsSpec != null) {\n      for (List<String> subtotalSpec : subtotalsSpec) {\n        for (String s : subtotalSpec) {\n          boolean found = false;\n          for (DimensionSpec ds : dimensions) {\n            if (s.equals(ds.getOutputName())) {\n              found = true;\n              break;\n            }\n          }\n          if (!found) {\n            throw new IAE(\n                \"Subtotal spec %s is either not a subset of top level dimensions.\",\n                subtotalSpec\n            );\n          }\n        }\n      }\n    }\n\n    return subtotalsSpec;\n  }\n\n  @JsonProperty\n  @Override\n  @JsonInclude(value = JsonInclude.Include.CUSTOM, valueFilter = VirtualColumns.JsonIncludeFilter.class)\n  public VirtualColumns getVirtualColumns()\n  {\n    return virtualColumns;\n  }","sourceCodeStart":251,"sourceCodeEnd":287,"githubUrl":"https://github.com/apache/druid/blob/9b90983fd291f26935af934383ce360473179e4d/processing/src/main/java/org/apache/druid/query/groupby/GroupByQuery.java#L251-L287","documentation":"GroupByQuery.verifySubtotalsSpec checks that each subtotal spec is a subset of the query's top-level dimensions. It throws IAE when a subtotal dimension cannot be found among the output names of the top-level dimension specs, meaning the subtotal references a non-existent or renamed dimension.","triggerScenarios":"Building or deserializing a GroupByQuery with subtotals containing a dimension name that is not in the top-level dimensionSpecs output names (including missing outputName aliases).","commonSituations":"SQL planning producing subtotals after dimension renaming; hand-written native queries where subtotal uses the underlying column name instead of the alias set via outputName.","solutions":["Change the subtotal spec to use the top-level dimensions' outputName values.","Add the missing dimension to the top-level dimensionSpecs, or align outputName with the subtotal name.","Validate subtotals against dimension output names in query-building code before constructing the query."],"exampleFix":"// before\ndimensions: [new DefaultDimensionSpec(\"col\", \"alias\")], subtotals: [[\"col\"]]\n// after\ndimensions: [new DefaultDimensionSpec(\"col\", \"alias\")], subtotals: [[\"alias\"]]","handlingStrategy":"validation","validationCode":"Set<String> dims = query.getDimensions().stream().map(DimensionSpec::getOutputName).collect(Collectors.toSet());\nboolean valid = query.getSubtotalsSpec().stream().allMatch(sub -> dims.containsAll(sub));","typeGuard":null,"tryCatchPattern":"try { GroupByQuery q = builder.build(); } catch (IllegalArgumentException e) { /* fix subtotals to use outputNames */ }","preventionTips":["Use dimension outputName (alias) values in subtotal specs","Validate subtotals against top-level dimension output names before building","Be careful with SQL planner output-name rewriting"],"tags":["druid","groupby","subtotals","validation"],"backgroundTag":"schema-validation-failed","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"}