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

  1. Upgrade the reading library and/or regenerate the file so statistics conform to the spec
  2. Disable predicate pushdown on boolean columns (or column indexes) to bypass statistics-based domain construction
  3. Rewrite the file with a standard writer (parquet-mr/Spark) so stats are populated correctly
  4. 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

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


AI-assisted analysis of prestodb/presto@55bb57d202 (2026-09-04). Data as JSON: /api/errors/3d4afd9f74ed8baf. Report an issue: GitHub.