apache/iceberg · error · java.lang.IllegalArgumentException

Unexpected object type for TIMESTAMP logical type. Received:

Error message

Unexpected object type for TIMESTAMP logical type. Received: ${object}

What it means

Thrown by AvroToRowDataConverters.convertToTimestamp when an Avro 'timestamp-millis'/'timestamp-micros' logical type field yields an object that is neither java.time.LocalDateTime nor a type supported by the Joda converter. The converter cannot map the runtime object to Flink TimestampData, so it fails fast with the offending object in the message.

Source

Thrown at flink/v2.1/flink/src/main/java/org/apache/iceberg/flink/formats/avro/AvroToRowDataConverters.java:251

        return TimestampData.fromEpochMillis(timeLong);
      } else if (precision <= 6) {
        return TimestampData.fromEpochMillis(
            Math.floorDiv(timeLong, 1000L), (int) Math.floorMod(timeLong, 1000L) * 1000);
      } else {
        // Iceberg: Added support for nanoseconds precision (FLINK-39251)
        return TimestampData.fromEpochMillis(
            Math.floorDiv(timeLong, 1_000_000L), (int) Math.floorMod(timeLong, 1_000_000L));
      }
    } else if (object instanceof Instant) {
      return TimestampData.fromInstant((Instant) object);
    } else if (object instanceof LocalDateTime) {
      return TimestampData.fromLocalDateTime((LocalDateTime) object);
    } else {
      JodaConverter jodaConverter = JodaConverter.getConverter();
      if (jodaConverter != null) {
        return TimestampData.fromEpochMillis(jodaConverter.convertTimestamp(object));
      } else {
        throw new IllegalArgumentException(
            "Unexpected object type for TIMESTAMP logical type. Received: " + object);
      }
    }
  }

  private static int convertToDate(Object object) {
    if (object instanceof Integer) {
      return (Integer) object;
    } else if (object instanceof LocalDate) {
      return (int) ((LocalDate) object).toEpochDay();
    } else {
      JodaConverter jodaConverter = JodaConverter.getConverter();
      if (jodaConverter != null) {
        return (int) jodaConverter.convertDate(object);
      } else {
        throw new IllegalArgumentException(
            "Unexpected object type for DATE logical type. Received: " + object);
      }

View on GitHub (pinned to 86d9c8fc54)

Solutions

  1. Add the joda-time dependency so JodaConverter can convert legacy objects, or re-register the Avro reader schema with a java.time-compatible value class
  2. Ensure the Avro specific/reflect record maps timestamp logical types to LocalDateTime (configure the reflect data model to use java.time)
  3. Convert the field yourself with a custom DatumReader/Conversion so the object reaching the converter is LocalDateTime
  4. Check the reader schema matches the writer schema's logical types

Example fix

// before (joda-time missing, legacy data)
Object obj = record.get("ts"); // java.util.Date
// after: add joda-time to the classpath or convert first
TimestampData ts = TimestampData.fromLocalDateTime(
    LocalDateTime.ofInstant(((java.util.Date) obj).toInstant(), ZoneOffset.UTC));
Defensive patterns

Strategy: type-guard

Validate before calling

Object ts = record.get("ts");
if (!(ts instanceof LocalDateTime) && !(ts instanceof java.util.Date) && !(ts instanceof Long)) {
  throw new IllegalStateException("Unexpected timestamp type: " + ts.getClass());
}

Type guard

boolean isSupportedTimestamp(Object o) {
  return o instanceof LocalDateTime || o instanceof java.util.Date || o instanceof Long;
}

Try / catch

try {
  RowData row = converter.convert(record);
} catch (IllegalArgumentException e) {
  if (e.getMessage().contains("Unexpected object type for TIMESTAMP")) {
    // normalize the object and retry
  } else { throw e; }
}

Prevention

When it happens

Trigger: Reading Avro data whose timestamp field deserialized to an unexpected type (e.g. java.util.Date, Long, org.joda.time.DateTime when Joda classes are absent from the classpath so JodaConverter.getConverter() returns null).

Common situations: Avro files written by frameworks that materialize timestamps as java.util.Date or epoch Long; running Flink 1.9+ (java.time default mapping) against records produced with the legacy Joda mapping without joda-time on the classpath.

Understand the failure class

Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.

Related errors


AI-assisted analysis of apache/iceberg@86d9c8fc54 (2026-09-12). Data as JSON: /api/errors/401b06147afd8a66. Report an issue: GitHub.