prestodb/presto · error · PrestoException

NOT_SUPPORTED

NOT_SUPPORTED

Error message

Parquet timestamp must be 12 bytes, actual 

What it means

ParquetTimestampUtils.getTimestampMillis only supports INT96 timestamps, which are always 12 bytes: 8 bytes for time-of-day nanos plus 4 bytes for the Julian day. If the supplied Binary has any other length the input is not an INT96 timestamp, and the library throws NOT_SUPPORTED rather than guessing the layout. Usually the column is actually INT64/INT32 (TIMESTAMP_MILLIS/MICROS or DATE) being routed through the INT96 decode path.

Source

Thrown at presto-parquet/src/main/java/com/facebook/presto/parquet/ParquetTimestampUtils.java:47

 */
public final class ParquetTimestampUtils
{
    private static final int JULIAN_EPOCH_OFFSET_DAYS = 2_440_588;
    private static final long MILLIS_IN_DAY = TimeUnit.DAYS.toMillis(1);
    private static final long NANOS_PER_MILLISECOND = TimeUnit.MILLISECONDS.toNanos(1);

    private ParquetTimestampUtils() {}

    /**
     * Returns GMT timestamp from binary encoded parquet timestamp (12 bytes - julian date + time of day nanos).
     *
     * @param timestampBinary INT96 parquet timestamp
     * @return timestamp in millis, GMT timezone
     */
    public static long getTimestampMillis(Binary timestampBinary)
    {
        if (timestampBinary.length() != 12) {
            throw new PrestoException(NOT_SUPPORTED, "Parquet timestamp must be 12 bytes, actual " + timestampBinary.length());
        }
        byte[] bytes = timestampBinary.getBytes();

        // little endian encoding - need to invert byte order
        long timeOfDayNanos = Longs.fromBytes(bytes[7], bytes[6], bytes[5], bytes[4], bytes[3], bytes[2], bytes[1], bytes[0]);
        int julianDay = Ints.fromBytes(bytes[11], bytes[10], bytes[9], bytes[8]);

        return julianDayToMillis(julianDay) + (timeOfDayNanos / NANOS_PER_MILLISECOND);
    }

    public static long getTimestampMillis(byte[] byteBuffer, int offset)
    {
        long timeOfDayNanos = BytesUtils.getLong(byteBuffer, offset);
        int julianDay = BytesUtils.getInt(byteBuffer, offset + 8);

        return julianDayToMillis(julianDay) + (timeOfDayNanos / NANOS_PER_MILLISECOND);
    }

View on GitHub (pinned to 55bb57d202)

Solutions

  1. Check the Parquet schema's physical type for the timestamp column; if it is INT64, decode it via the INT64 timestamp path instead of getTimestampMillis.
  2. If you control the writer, configure it to emit INT96 timestamps, or migrate the reader to support INT64 (TIMESTAMP_MILLIS/MICROS) annotations.
  3. Ensure the Presto/Parquet type mapping maps the column to the right timestamp type so the correct decoder is selected.
  4. Validate binary length before calling: only pass 12-byte binaries to getTimestampMillis.

Example fix

// before
long millis = ParquetTimestampUtils.getTimestampMillis(binary); // 8-byte INT64 value -> throws
// after
if (binary.length() == 12) {
    millis = ParquetTimestampUtils.getTimestampMillis(binary);
} else {
    millis = decodeInt64Timestamp(binary, isAdjustedToUTC); // use INT64 path
}
Defensive patterns

Strategy: type-guard

Validate before calling

// check physical type before decoding
if (columnDescriptor.getPrimitiveType().getPrimitiveTypeName() != PrimitiveTypeName.INT96) {
    throw new IllegalArgumentException("Column is not INT96: " + columnDescriptor.getPrimitiveType().getPrimitiveTypeName());
}

Type guard

boolean isInt96Timestamp(Binary b) { return b != null && b.length() == 12; }

Try / catch

try {
    return ParquetTimestampUtils.getTimestampMillis(binary);
} catch (PrestoException e) {
    if (e.getErrorCode().getCode() == NOT_SUPPORTED.toErrorCode().getCode()) {
        return decodeInt64Timestamp(binary); // fallback path
    }
    throw e;
}

Prevention

When it happens

Trigger: Calling getTimestampMillis with a Binary whose length() != 12 — e.g. an 8-byte INT64 timestamp-micros/millis value or a 4-byte DATE value passed to the INT96 decoder.

Common situations: Files written with useDeprecatedLogicalTimestamp=false (modern writers emit INT64 timestamps); spark.sql.parquet.int96TimestampConversion / writer version differences; schema mapping config that routes an INT64 timestamp column into the INT96 read path.

Understand the failure class

Background: Presto NOT_SUPPORTED error: what "not supported" means and how to fix it — this error's family across 3 libraries.

Related errors


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