apache/beam · error · UnsupportedOperationException
Unsupported Beam type for Iceberg timestamp with timezone: {
Error message
Unsupported Beam type for Iceberg timestamp with timezone: {valueClass} What it means
getIcebergTimestampValue converts a Beam value into an Iceberg timestamptz (OffsetDateTime). It accepts java.time.Instant, Long (micros since epoch), and ISO-8601 String; any other Beam runtime type is rejected with UnsupportedOperationException so a silent data corruption never reaches the Iceberg table.
Source
Thrown at sdks/java/io/iceberg/src/main/java/org/apache/beam/sdk/io/iceberg/IcebergUtils.java:487
private static Object getIcebergTimestampValue(Object beamValue, boolean shouldAdjustToUtc) {
// timestamptz
if (shouldAdjustToUtc) {
if (beamValue instanceof java.time.Instant) { // MicrosInstant
OffsetDateTime epoch = java.time.Instant.ofEpochSecond(0).atOffset(ZoneOffset.UTC);
java.time.Instant instant = (java.time.Instant) beamValue;
long nanosFromEpoch =
TimeUnit.SECONDS.toNanos(instant.getEpochSecond()) + instant.getNano();
return ChronoUnit.NANOS.addTo(epoch, nanosFromEpoch);
} else if (beamValue instanceof LocalDateTime) { // SqlTypes.DATETIME
return OffsetDateTime.of((LocalDateTime) beamValue, ZoneOffset.UTC);
} else if (beamValue instanceof Instant) { // FieldType.DATETIME
return DateTimeUtil.timestamptzFromMicros(((Instant) beamValue).getMillis() * 1000L);
} else if (beamValue instanceof Long) { // FieldType.INT64
return DateTimeUtil.timestamptzFromMicros((Long) beamValue);
} else if (beamValue instanceof String) { // FieldType.STRING
return OffsetDateTime.parse((String) beamValue).withOffsetSameInstant(ZoneOffset.UTC);
} else {
throw new UnsupportedOperationException(
"Unsupported Beam type for Iceberg timestamp with timezone: " + beamValue.getClass());
}
}
// timestamp
if (beamValue instanceof java.time.Instant) { // MicrosInstant
java.time.Instant instant = (java.time.Instant) beamValue;
return DateTimeUtil.timestampFromNanos(
TimeUnit.SECONDS.toNanos(instant.getEpochSecond()) + instant.getNano());
} else if (beamValue instanceof LocalDateTime) { // SqlType.DATETIME
return beamValue;
} else if (beamValue instanceof Instant) { // FieldType.DATETIME
return DateTimeUtil.timestampFromMicros(((Instant) beamValue).getMillis() * 1000L);
} else if (beamValue instanceof Long) { // FieldType.INT64
return DateTimeUtil.timestampFromMicros((Long) beamValue);
} else if (beamValue instanceof String) { // FieldType.STRING
return LocalDateTime.parse((String) beamValue);
} else {View on GitHub (pinned to 12126d8942)
Solutions
- Check the Beam Row field's declared FieldType: for a timestamptz column use FieldType.DATETIME with values that are java.time.Instant (or FieldType.INT64 micros / FieldType.STRING ISO-8601)
- Convert the value before writing: if you have a joda DateTime call instant.toInstant() or new java.time.Instant(...); for java.util.Date use date.toInstant()
- If the value is a String, ensure it is ISO-8601 parseable by OffsetDateTime.parse, otherwise pre-normalize it
- Log the offending class (beamValue.getClass()) and trace where the Row was created to fix the producer
Example fix
// before (producer built joda-based value) Row row = Row.withSchema(schema).addValues(DateTime.now()).build(); // after Row row = Row.withSchema(schema).addValues(java.time.Instant.now()).build();
Defensive patterns
Strategy: type-guard
Validate before calling
if (!(v instanceof java.time.Instant) && !(v instanceof Long) && !(v instanceof String)) {
throw new IllegalArgumentException("Field '" + name + "' must be Instant/Long/String for timestamptz, got " + v.getClass());
} Type guard
boolean isTimestamptzCompatible(Object v) {
return v instanceof java.time.Instant || v instanceof Long || v instanceof String;
} Try / catch
try {
icebergRecord = copyFieldIntoRecord(...);
} catch (UnsupportedOperationException e) {
LOG.error("timestamptz conversion failed: {}", e.getMessage());
// coerce value to Instant or skip/retry with corrected schema
} Prevention
- Use FieldType.DATETIME with java.time.Instant values in Beam Rows
- Never put joda DateTime or java.util.Date into Rows destined for Iceberg timestamptz columns
- Unit-test row conversion for every timestamp-producing source
- Keep Strings strictly ISO-8601 with offset info
When it happens
Trigger: copyFieldIntoRecord maps a Beam Row field into an Iceberg record for a timestamptz column, but the Beam value is none of Instant, Long, or String — e.g. a Beam Row built with FieldType.DATETIME (org.joda.time.DateTime / ReadableInstant other than Instant), a boxed Integer, a byte array, or a custom logical type object.
Common situations: Reading records from another source (e.g. Avro/Parquet/JDBC) whose date-time decoder produced a different class (joda DateTime, java.util.Date, Timestamp); a Beam schema inferred a LOGICAL_TYPE whose runtime representation is not Instant; someone hand-built Rows with the wrong FieldType mapping.
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
- Unsupported Iceberg type for Beam type DATETIME: {valueClass
- Unsupported row type: {valueClass}
- Unexpected Beam type: {}
- Unable to provide coder for %s, this factory can only provid
- The input schema must have exactly one field of type byte.
AI-assisted analysis of apache/beam@12126d8942 (2026-09-13).
Data as JSON: /api/errors/8c3e25f6cd21bf40.
Report an issue: GitHub.