apache/iceberg · error · IllegalArgumentException

Unexpected object type for TIMESTAMP logical type. Received:

Error message

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

What it means

convertToTimestamp converts an Avro field value for a TIMESTAMP logical type into Flink TimestampData. It accepts LocalDateTime, java.util.Date/Instant via Joda bridge, etc.; when the object is none of the expected classes (and no Joda converter is available) it throws IllegalArgumentException reporting the actual object's toString.

Source

Thrown at flink/v1.20/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. Align the table's avro timestamp-mapping property with how the data files were actually written (rewrite files with the Spark/Avro rewrite procedure if mismatched).
  2. Check the 'Received:' value in the message to identify the actual Java class and adjust the reader/converter for that class.
  3. Upgrade or downgrade the iceberg-flink version so the converter matches the file's logical-type encoding (FLINK-39251 nanosecond support changed expectations).
  4. Pre-convert the field: wrap the raw object into LocalDateTime before it reaches the Avro→RowData converter (custom converter via createConverter overloads).

Example fix

// before
Object avroValue = record.get("ts"); // java.time.Instant (not LocalDateTime)
RowData row = converter.convert(record); // throws
// after
record.put("ts", LocalDateTime.ofInstant((Instant) avroValue, ZoneOffset.UTC));
RowData row = converter.convert(record);
Defensive patterns

Strategy: validation

Validate before calling

Object v = record.get("ts");
if (!(v instanceof LocalDateTime) && !jodaSupported(v)) { throw new IllegalArgumentException("ts has unexpected class: " + v.getClass()); }

Type guard

boolean isSupportedTimestamp(Object v) { return v instanceof LocalDateTime || v instanceof org.joda.time.DateTime || v instanceof java.util.Date; }

Try / catch

try { return convertToTimestamp(obj, type); } catch (IllegalArgumentException e) { return TimestampData.fromLocalDateTime(toLocalDateTime(obj)); }

Prevention

When it happens

Trigger: Avro record contains a value under a timestamp logical-type field whose Java class is neither LocalDateTime nor a type handled by JodaConverter — typically a Long in legacy micros read with non-legacy converter expectations, or vice versa.

Common situations: Mismatch between the table's avro timestamp mapping property and the actual Avro file contents (files written with legacy Long encoding read via the modern converter); schema evolution changing field encoding without rewriting files.

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/2980bb1fab2e788f. Report an issue: GitHub.