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.
Solutions
- 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
- Ensure the Avro specific/reflect record maps timestamp logical types to LocalDateTime (configure the reflect data model to use java.time)
- Convert the field yourself with a custom DatumReader/Conversion so the object reaching the converter is LocalDateTime
- 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
- Keep joda-time on the classpath when consuming legacy Avro data
- Align reader/writer Avro schemas and logical type conversions
- Prefer specific records generated with java.time mappings
- Validate sample records' runtime types before running full jobs
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
- Unexpected object type for TIMESTAMP logical type. Received
- Unexpected object type for TIMESTAMP logical type…
- Unexpected object type for TIMESTAMP logical type…
- Avro does not support LOCAL TIMESTAMP type with precision
- Avro does not support LOCAL TIMESTAMP type with precision…
AI-assisted analysis of apache/iceberg@86d9c8fc54 (2026-09-12).
Data as JSON: /api/errors/401b06147afd8a66.
Report an issue: GitHub.
Appendix: 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)