prestodb/presto · warning · VerifyException
Impossible boolean statistics
Error message
Impossible boolean statistics
What it means
When building a predicate Domain from Parquet boolean column statistics, getDomain assumes at least one of hasTrueValues/hasFalseValues is set once nulls-only cases were handled earlier. Reaching the final fall-through means statistics were internally inconsistent (neither true nor false values recorded despite non-null statistics), which should be impossible, so VerifyException is thrown to surface corrupted/contradictory stats.
Source
Thrown at presto-parquet/src/main/java/com/facebook/presto/parquet/predicate/TupleDomainParquetPredicate.java:159
boolean hasNullValue)
{
checkArgument(minimums.size() == maximums.size(), "Expected minimums and maximums to have the same size");
List<Range> ranges = new ArrayList<>();
if (type.equals(BOOLEAN)) {
boolean hasTrueValues = minimums.stream().anyMatch(value -> (boolean) value) || maximums.stream().anyMatch(value -> (boolean) value);
boolean hasFalseValues = minimums.stream().anyMatch(value -> !(boolean) value) || maximums.stream().anyMatch(value -> !(boolean) value);
if (hasTrueValues && hasFalseValues) {
return Domain.all(type);
}
if (hasTrueValues) {
return Domain.create(ValueSet.of(type, true), hasNullValue);
}
if (hasFalseValues) {
return Domain.create(ValueSet.of(type, false), hasNullValue);
}
// All nulls case is handled earlier
throw new VerifyException("Impossible boolean statistics");
}
if ((type.equals(BIGINT) || type.equals(TINYINT) || type.equals(SMALLINT) || type.equals(INTEGER))) {
for (int i = 0; i < minimums.size(); i++) {
long min = asLong(minimums.get(i));
long max = asLong(maximums.get(i));
if (isStatisticsOverflow(type, min, max)) {
return Domain.create(ValueSet.all(type), hasNullValue);
}
ranges.add(Range.range(type, min, true, max, true));
}
checkArgument(!ranges.isEmpty(), "cannot use empty ranges");
return Domain.create(ValueSet.ofRanges(ranges), hasNullValue);
}
if (type.equals(REAL)) {
for (int i = 0; i < minimums.size(); i++) {View on GitHub (pinned to 55bb57d202)
Solutions
- Upgrade the reading library and/or regenerate the file so statistics conform to the spec
- Disable predicate pushdown on boolean columns (or column indexes) to bypass statistics-based domain construction
- Rewrite the file with a standard writer (parquet-mr/Spark) so stats are populated correctly
- Validate the file's column indexes with parquet-tools
Example fix
// before
throw new VerifyException("Impossible boolean statistics");
// after
default:
return Domain.create(ValueSet.all(type), hasNullValue); // tolerate broken stats Defensive patterns
Strategy: fallback
Try / catch
try {
domain = buildDomainFromStats(stats, type);
} catch (VerifyException e) {
if (e.getMessage().equals("Impossible boolean statistics")) {
domain = Domain.create(ValueSet.all(type), true); // no pushdown, safe fallback
} else throw e;
} Prevention
- Regenerate files with conformant writers that populate boolean min/max stats correctly
- Disable column-index pushdown for sources known to emit broken stats
- Treat statistics as hints only — never assume internal consistency
When it happens
Trigger: getDomain on a BOOLEAN-typed column index/statistics where hasNulls was handled but both hasTrueValues and hasFalseValues are false — i.e. statistics claim non-null presence without any true/false values, typically from malformed column index data.
Common situations: Corrupted or nonconformant Parquet column indexes written by buggy writers; stats-only (no rows) row groups with contradictory metadata; reading files written by tools that emit empty boolean statistics objects.
Related errors
- Can't convert value to long:
- We didn't read correct number of definitionLevels
- Still remaining to be read in current batch.
- Corrupted Parquet file: extra %d values to be consumed when
- Still remaining to be read in current batch.
AI-assisted analysis of prestodb/presto@55bb57d202 (2026-09-04).
Data as JSON: /api/errors/3d4afd9f74ed8baf.
Report an issue: GitHub.