apache/iceberg · error · java.lang.UnsupportedOperationException

Unsupported ZonedTimestampType.

Error message

Unsupported ZonedTimestampType.

What it means

FlinkTypeVisitor is the abstract visitor mapping Flink logical types to Iceberg types; it provides a default visit(ZonedTimestampType) that throws UnsupportedOperationException because TIMESTAMP WITH LOCAL TIME ZONE has no direct Iceberg mapping in this conversion path. Subclasses may override, but by default encountering this type aborts the conversion.

Source

Thrown at flink/v2.1/flink/src/main/java/org/apache/iceberg/flink/FlinkTypeVisitor.java:38

import org.apache.flink.table.types.logical.DayTimeIntervalType;
import org.apache.flink.table.types.logical.DistinctType;
import org.apache.flink.table.types.logical.LogicalType;
import org.apache.flink.table.types.logical.LogicalTypeVisitor;
import org.apache.flink.table.types.logical.NullType;
import org.apache.flink.table.types.logical.RawType;
import org.apache.flink.table.types.logical.StructuredType;
import org.apache.flink.table.types.logical.SymbolType;
import org.apache.flink.table.types.logical.YearMonthIntervalType;
import org.apache.flink.table.types.logical.ZonedTimestampType;

public abstract class FlinkTypeVisitor<T> implements LogicalTypeVisitor<T> {

  // ------------------------- Unsupported types ------------------------------

  @Override
  public T visit(ZonedTimestampType zonedTimestampType) {
    throw new UnsupportedOperationException("Unsupported ZonedTimestampType.");
  }

  @Override
  public T visit(YearMonthIntervalType yearMonthIntervalType) {
    throw new UnsupportedOperationException("Unsupported YearMonthIntervalType.");
  }

  @Override
  public T visit(DayTimeIntervalType dayTimeIntervalType) {
    throw new UnsupportedOperationException("Unsupported DayTimeIntervalType.");
  }

  @Override
  public T visit(DistinctType distinctType) {
    throw new UnsupportedOperationException("Unsupported DistinctType.");
  }

  @Override

View on GitHub (pinned to 86d9c8fc54)

Solutions

  1. Cast the column to TIMESTAMP (without local time zone) before writing to Iceberg.
  2. Override visit(ZonedTimestampType) in a custom visitor if a specific mapping is needed.
  3. Express the value as TIMESTAMP and store the timezone semantics out-of-band.

Example fix

// before
CREATE TABLE t (ts TIMESTAMP(3) WITH LOCAL TIME ZONE) -- iceberg sink
// after
CREATE TABLE t (ts TIMESTAMP(3)) -- cast: CAST(ts AS TIMESTAMP(3))
Defensive patterns

Strategy: validation

Validate before calling

schema.getColumns().forEach(c -> {
  if (c.getDataType().getLogicalType() instanceof ZonedTimestampType) {
    throw new IllegalArgumentException("Cast TIMESTAMP WITH LOCAL TIME ZONE before Iceberg write: " + c.getName());
  }
});

Type guard

boolean isZonedTimestamp(LogicalType t) { return t instanceof ZonedTimestampType; }

Try / catch

try {
  RowType rowType = FlinkSchemaUtil.toType(rowDataType);
} catch (UnsupportedOperationException e) {
  // inspect schema for TIMESTAMP_LTZ columns and cast them
}

Prevention

When it happens

Trigger: Any Flink-to-Iceberg type conversion (e.g. FlinkSchemaUtil.toType / fromFlinkSchema) traversing a field of type TIMESTAMP(3) WITH LOCAL TIME ZONE (ZonedTimestampType) using the default visitor behavior.

Common situations: Tables with columns sourced from CDC/other connectors using TIMESTAMP_LTZ; writing such a schema into Iceberg without first casting to TIMESTAMP.

Related errors


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