prestodb/presto · warning · IllegalArgumentException

Can't convert value to long:

Error message

Can't convert value to long: 

What it means

asLong converts Parquet statistic values (min/max) to long for integer-family types (BIGINT, INTEGER, SMALLINT, TINYINT). It accepts only Byte/Short/Integer/Long; any other value class (e.g. BigDecimal, BigInteger, String from odd statistic decoders) is rejected with IllegalArgumentException naming the actual class.

Solutions

  1. Ensure the parquet statistics deserialization matches the column physical type (upgrade/align parquet library versions)
  2. Check whether the column's declared type was altered (e.g. INT64 read as DECIMAL) and re-map it correctly
  3. Validate file statistics with parquet-tools; rewrite the file if the stats are corrupt
  4. Catch the IllegalArgumentException where statistics are consumed and fall back to an all-values domain

Example fix

// before
throw new IllegalArgumentException("Can't convert value to long: " + value.getClass().getName());
// after
if (value instanceof Number) {
    return ((Number) value).longValue();
}
if (value instanceof BigInteger) {
    return ((BigInteger) value).longValueExact();
}
throw new IllegalArgumentException("Can't convert value to long: " + value.getClass().getName());
Defensive patterns

Strategy: type-guard

Validate before calling

// check statistic value classes before domain construction
if (stats.getMin() != null && !(stats.getMin() instanceof Number)) {
    skipPredicatePushdown(column); // fall back to full scan
}

Type guard

boolean isIntegralStat(Object v) {
    return v instanceof Byte || v instanceof Short || v instanceof Integer || v instanceof Long;
}

Try / catch

try {
    domain = TupleDomainParquetPredicate.asLong(minStat);
} catch (IllegalArgumentException e) {
    if (e.getMessage().startsWith("Can't convert value to long")) {
        domain = Domain.create(ValueSet.all(BIGINT), true);
    } else throw e;
}

Prevention

When it happens

Trigger: TupleDomainParquetPredicate builds domains for BIGINT/TINYINT/SMALLINT/INTEGER columns and calls asLong on a statistics minimum/maximum whose runtime type is not one of Byte, Short, Integer, or Long — i.e. the statistics deserializer produced an unexpected boxed type.

Common situations: Statistics decoded by a mismatched parquet reader version producing BigInteger/BigDecimal for INT64; corrupted statistics blobs decoded as strings; custom type mappings where min/max are deserialized differently than expected.

Related errors


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

Appendix: source

Thrown at presto-parquet/src/main/java/com/facebook/presto/parquet/predicate/TupleDomainParquetPredicate.java:270

        int dictionarySize = dictionaryPage.get().getDictionarySize();
        DictionaryValueConverter converter = new DictionaryValueConverter(dictionary);
        Function<Integer, Object> convertFunction = converter.getConverter(columnDescriptor.getPrimitiveType());
        List<Object> values = new ArrayList<>();
        for (int i = 0; i < dictionarySize; i++) {
            values.add(convertFunction.apply(i));
        }

        // TODO: when min == max (i.e., singleton ranges, the construction of Domains can be done more efficiently
        return getDomain(columnDescriptor, type, values, values, true);
    }

    public static long asLong(Object value)
    {
        if (value instanceof Byte || value instanceof Short || value instanceof Integer || value instanceof Long) {
            return ((Number) value).longValue();
        }

        throw new IllegalArgumentException("Can't convert value to long: " + value.getClass().getName());
    }

    private static <T extends Comparable<T>> Domain createDomain(Type type, ColumnIndex columnIndex, boolean hasNullValue, List<T> mins, List<T> maxs)
    {
        if (mins.isEmpty() || maxs.isEmpty() || mins.size() != maxs.size()) {
            return Domain.create(ValueSet.all(type), hasNullValue);
        }
        int pageCount = columnIndex.getMinValues().size();
        List<Range> ranges = new ArrayList<>();
        for (int i = 0; i < pageCount; i++) {
            T min = mins.get(i);
            T max = maxs.get(i);
            if (min.compareTo(max) > 0) {
                return Domain.create(ValueSet.all(type), hasNullValue);
            }

            if (min instanceof Long) {
                if (isStatisticsOverflow(type, asLong(min), asLong(max))) {

View on GitHub (pinned to 55bb57d202)