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
- 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.
- If you control the writer, configure it to emit INT96 timestamps, or migrate the reader to support INT64 (TIMESTAMP_MILLIS/MICROS) annotations.
- Ensure the Presto/Parquet type mapping maps the column to the right timestamp type so the correct decoder is selected.
- 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
- Inspect the Parquet schema's physical type for timestamp columns before choosing a decoder.
- Set writer config explicitly (INT96 vs INT64) and mirror it in reader type mapping.
- Unit-test timestamp columns written by each writer version in your fleet.
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
- NOT_SUPPORTED
- NOT_SUPPORTED
- Codec not supported in Parquet:
- PARQUET_IO_READ_ERROR
- Still remaining to be read in current batch.
AI-assisted analysis of prestodb/presto@55bb57d202 (2026-09-04).
Data as JSON: /api/errors/e1d5553a619d38ca.
Report an issue: GitHub.