apache/druid · error · DruidException
clusteringColumns must be the leading prefix of columns, in
Error message
clusteringColumns must be the leading prefix of columns, in order; got %s vs columns prefix %s
What it means
ClusteredValueGroupsBaseTableProjectionSpec.validate rejects projection specs whose clusteringColumns do not form an exact leading prefix of columns in the same order. Druid's clusteredValueGroups base-table projection encodes clustering as the first N columns of the projection's column list, so a mismatch (extra clustering columns, or columns out of order) makes the spec invalid. The message shows the full columns list versus the clustering prefix actually found.
Source
Thrown at processing/src/main/java/org/apache/druid/data/input/impl/ClusteredValueGroupsBaseTableProjectionSpec.java:301
}
}
return builder()
.virtualColumns(VirtualColumns.create(remaining))
.clusteringColumns(clusteringColumns)
.columns(columns)
.build();
}
private static void validate(List<DimensionSchema> columns, List<String> clusteringColumns)
{
if (CollectionUtils.isNullOrEmpty(clusteringColumns)) {
throw InvalidInput.exception("clusteringColumns must be non-empty for clusteredValueGroups base table");
}
if (CollectionUtils.isNullOrEmpty(columns)) {
throw InvalidInput.exception("columns must be non-empty for clusteredValueGroups base table");
}
if (clusteringColumns.size() > columns.size()) {
throw clusteringPrefixException(columns, clusteringColumns);
}
for (int i = 0; i < clusteringColumns.size(); i++) {
final DimensionSchema clusteringColumn = columns.get(i);
if (!clusteringColumn.getName().equals(clusteringColumns.get(i))) {
throw clusteringPrefixException(columns, clusteringColumns);
}
// Clustering values are dictionary-encoded into per-type dictionaries on the write side, which supports only
// these scalar types; reject anything else up front rather than failing later at ingest.
if (!Projections.isAllowedClusteringType(clusteringColumn.getColumnType())) {
throw InvalidInput.exception(
"clustering column [%s] has unsupported type [%s]; clustering columns must be STRING, LONG, DOUBLE, or FLOAT",
clusteringColumn.getName(),
clusteringColumn.getColumnType()
);
}
}
final Set<String> seen = Sets.newHashSetWithExpectedSize(columns.size());View on GitHub (pinned to 9b90983fd2)
Solutions
- Reorder columns so the clustering columns come first, in exactly the same order as clusteringColumns.
- Ensure every clustering column appears in columns with an identical name.
- Remove clustering columns that are not part of the leading prefix, or add them to columns if intended.
- Validate the projection spec JSON (names and order) before submitting the ingestion/projection spec.
Example fix
// before: mismatched order clusteringColumns: ["country", "city"] columns: ["city", "country", "ts"] // after: clustering columns are the ordered prefix clusteringColumns: ["country", "city"] columns: ["country", "city", "ts"]
Defensive patterns
Strategy: validation
Validate before calling
// ensure clusteringColumns is an ordered prefix of column names before building the spec
List<String> names = columns.stream().map(DimensionSchema::getName).collect(Collectors.toList());
if (names.size() < clusteringColumns.size() ||
!names.subList(0, clusteringColumns.size()).equals(clusteringColumns)) {
throw new IllegalArgumentException("clusteringColumns must be a leading prefix of columns, in order");
} Try / catch
try { new ClusteredValueGroupsBaseTableProjectionSpec(...); } catch (DruidException e) { if (e.getMessage().contains("leading prefix")) { fixColumnOrder(); } throw e; } Prevention
- Generate columns from clusteringColumns plus remaining columns so the prefix relation holds by construction.
- Never reorder columns without reordering clusteringColumns.
- Unit-test projection spec builders with the exact prefix invariant.
When it happens
Trigger: Building a ClusteredValueGroupsBaseTableProjectionSpec (directly or via projection spec JSON) where clusteringColumns.size() > columns.size(), or where columns.get(i).getName() != clusteringColumns.get(i) for some i < clusteringColumns.size().
Common situations: Hand-written projection specs listing clustering columns not present in columns; reordering columns without reordering clusteringColumns; appending clustering columns beyond the declared column list; tooling generating the two lists independently.
Understand the failure class
Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.
Related errors
- Cannot simultaneously replace and append to existing segment
- GranularitySpec's intervals cannot be empty for replace.
- Aggregation [%s] does not support column [%s] of type [%s].
- ColumnCapacityExceededException
- Cannot accept both 'splitPoints' and 'numBins'
AI-assisted analysis of apache/druid@9b90983fd2 (2026-09-07).
Data as JSON: /api/errors/6c5d4331f991a9cd.
Report an issue: GitHub.