apache/druid · error · SegmentValidationException
Metric names differ. Expected
Error message
Metric names differ. Expected [%s] found [%s]
What it means
IndexIO.validateTwoSegments also compares metric (aggregator) name sets between the two segment adapters. This SegmentValidationException is thrown when the metric name sets differ, indicating the segments have different aggregator columns.
Solutions
- Compare the metric sets in the message and identify the missing/extra metric.
- Make both ingestion/compaction specs declare the same aggregators and rebuild the divergent segment.
- If a metric was intentionally added/renamed, validate against the updated reference segment, not the old one.
- Check query-time metric availability (queries depending on the missing metric will fail) after fixing.
Defensive patterns
Strategy: validation
Validate before calling
Set<String> met1 = Sets.newHashSet(adapter1.getMetricNames());
Set<String> met2 = Sets.newHashSet(adapter2.getMetricNames());
if (!met1.equals(met2)) {
throw new IllegalStateException("Metric sets differ: " + met1 + " vs " + met2);
} Try / catch
try {
io.validateTwoSegments(adapter1, adapter2);
} catch (SegmentValidationException e) {
if (e.getMessage().startsWith("Metric names differ")) {
log.error("Aggregator mismatch between segments: %s", e.getMessage());
}
throw e;
} Prevention
- Keep the aggregators list identical across ingestion/compaction runs of the same datasource.
- Review aggregator renames before rebuilding segments.
- Generate compaction specs from the original segment's metrics to avoid drift.
- Validate metric sets yourself before row-level comparison for clearer errors.
When it happens
Trigger: validateTwoSegments where adapter1.getMetricNames() and adapter2.getMetricNames() yield unequal sets — a metric present in one segment but missing in the other.
Common situations: Changing the aggregators list between ingestion runs; a metricsInSpec typo or renamed aggregator; compaction producing different metrics than the original segment; comparing segments from different datasources or specs.
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
- Dimension names differ. Expected
- Row count mismatch. Expected
- announceHistoricalSegments failed with null metadata…
- Can't find lock for the interval of segment
- Cannot find a version for interval
AI-assisted analysis of apache/druid@9b90983fd2 (2026-09-07).
Data as JSON: /api/errors/51c47ac236a8d306.
Report an issue: GitHub.
Appendix: source
Thrown at processing/src/main/java/org/apache/druid/segment/IndexIO.java:172
"Row count mismatch. Expected [%d] found [%d]",
adapter1.getNumRows(),
adapter2.getNumRows()
);
}
{
final Set<String> dimNames1 = Sets.newHashSet(adapter1.getDimensionNames(true));
final Set<String> dimNames2 = Sets.newHashSet(adapter2.getDimensionNames(true));
if (!dimNames1.equals(dimNames2)) {
throw new SegmentValidationException(
"Dimension names differ. Expected [%s] found [%s]",
dimNames1,
dimNames2
);
}
final Set<String> metNames1 = Sets.newHashSet(adapter1.getMetricNames());
final Set<String> metNames2 = Sets.newHashSet(adapter2.getMetricNames());
if (!metNames1.equals(metNames2)) {
throw new SegmentValidationException("Metric names differ. Expected [%s] found [%s]", metNames1, metNames2);
}
}
try (
final RowIterator it1 = adapter1.getRows();
final RowIterator it2 = adapter2.getRows()
) {
long row = 0L;
while (it1.moveToNext()) {
if (!it2.moveToNext()) {
throw new SegmentValidationException("Unexpected end of second adapter");
}
final RowPointer rp1 = it1.getPointer();
final RowPointer rp2 = it2.getPointer();
++row;
if (rp1.getRowNum() != rp2.getRowNum()) {
throw new SegmentValidationException("Row number mismatch: [%d] vs [%d]", rp1.getRowNum(), rp2.getRowNum());
}
try {View on GitHub (pinned to 9b90983fd2)