apache/iceberg · error · IllegalArgumentException

Unexpected object type for TIME logical type. Received: ${ob

Error message

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

What it means

convertToTime converts an Avro TIME logical-type value into millis-of-day for Flink TIME. Expected inputs are LocalTime or Joda-handled types; anything else (with no Joda converter available) triggers this IllegalArgumentException identifying the actual object.

Source

Thrown at flink/v1.20/flink/src/main/java/org/apache/iceberg/flink/formats/avro/AvroToRowDataConverters.java:284

      } else {
        throw new IllegalArgumentException(
            "Unexpected object type for DATE logical type. Received: " + object);
      }
    }
  }

  private static int convertToTime(Object object) {
    final int millis;
    if (object instanceof Integer) {
      millis = (Integer) object;
    } else if (object instanceof LocalTime) {
      millis = ((LocalTime) object).get(ChronoField.MILLI_OF_DAY);
    } else {
      JodaConverter jodaConverter = JodaConverter.getConverter();
      if (jodaConverter != null) {
        millis = jodaConverter.convertTime(object);
      } else {
        throw new IllegalArgumentException(
            "Unexpected object type for TIME logical type. Received: " + object);
      }
    }
    return millis;
  }

  private static byte[] convertToBytes(Object object) {
    if (object instanceof GenericFixed) {
      return ((GenericFixed) object).bytes();
    } else if (object instanceof ByteBuffer) {
      ByteBuffer byteBuffer = (ByteBuffer) object;
      byte[] bytes = new byte[byteBuffer.remaining()];
      byteBuffer.get(bytes);
      return bytes;
    } else {
      return (byte[]) object;
    }
  }

View on GitHub (pinned to 86d9c8fc54)

Solutions

  1. Align the table's time-unit mapping property with the actual file encoding and rewrite files if necessary.
  2. Add the joda-time dependency when reading legacy Joda-encoded TIME values.
  3. Check the 'Received:' class in the message and pre-convert that value (e.g. Long millis → LocalTime) before conversion.

Example fix

// before
Object v = record.get("t"); // Long millis
RowData row = converter.convert(record); // throws
// after
record.put("t", LocalTime.ofNanoOfDay((Long) v * 1_000_000));
RowData row = converter.convert(record);
Defensive patterns

Strategy: validation

Validate before calling

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

Type guard

boolean isSupportedTime(Object v) { return v instanceof LocalTime || v instanceof org.joda.time.LocalTime; }

Try / catch

try { return convertToTime(obj); } catch (IllegalArgumentException e) { return (int) toLocalTime(obj).get(ChronoField.MILLI_OF_DAY); }

Prevention

When it happens

Trigger: Avro TIME field decoded as a class other than LocalTime (e.g. Long micros/millis) while legacyTimestampMapping expectations differ, or joda-time missing so JodaConverter is null for legacy Joda-encoded times.

Common situations: Files written with legacy time-unit encoding read by a converter expecting modern types; missing joda-time dependency; schema property mapping changed without data rewrite.

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