apache/iceberg · error · java.lang.UnsupportedOperationException

Failed to read Variant %s of type %s as timestamp

Error message

Failed to read Variant %s of type %s as timestamp

What it means

timestampValue converts a Variant to TimestampData only for recognized timestamp variant types (with micros/nanos precision); any other Variant type throws this UnsupportedOperationException via errMsg(variant, "timestamp").

Source

Thrown at flink/v2.1/flink/src/main/java/org/apache/iceberg/flink/data/VariantRowDataWrapper.java:295

  private static DecimalData decimalDataValue(Variant variant, DecimalType decimalType) {
    return DecimalData.fromBigDecimal(
        variant.getDecimal(), decimalType.getPrecision(), decimalType.getScale());
  }

  private static TimestampData timestampValue(Variant variant, int precision) {
    return switch (variant.getType()) {
      case TIMESTAMP -> TimestampData.fromLocalDateTime(variant.getDateTime());
      case TIMESTAMP_LTZ -> TimestampData.fromInstant(variant.getInstant());
      case BIGINT -> {
        Preconditions.checkArgument(
            precision >= MICROSECOND_PRECISION && precision <= NANOSECOND_PRECISION,
            "Invalid precision: %s. Only micros and nanos precision are supported.",
            precision);
        yield precision > MICROSECOND_PRECISION
            ? nanoTimestampValue(variant.getLong())
            : microTimestampValue(variant.getLong());
      }
      default -> throw new UnsupportedOperationException(errMsg(variant, "timestamp"));
    };
  }

  private static TimestampData microTimestampValue(long micros) {
    return TimestampData.fromEpochMillis(
        Math.floorDiv(micros, 1000L), (int) Math.floorMod(micros, 1000L) * 1000);
  }

  private static TimestampData nanoTimestampValue(long nanos) {
    return TimestampData.fromEpochMillis(
        Math.floorDiv(nanos, 1_000_000L), (int) Math.floorMod(nanos, 1_000_000L));
  }

  private Variant field(int position) {
    String fieldName = rowType.getFields().get(position).getName();
    return variantData.getField(fieldName);
  }

View on GitHub (pinned to 86d9c8fc54)

Solutions

  1. Ensure the writer stores a proper timestamp-typed Variant (not a raw long/string)
  2. Convert the value to a timestamp variant upstream before writing
  3. Use getLong and construct TimestampData manually if the column actually holds epoch values

Example fix

// before
row.getTimestamp(7, 6); // variant holds epoch millis as LONG -> throws
// after
TimestampData ts = TimestampData.fromEpochMillis(row.getLong(7));
Defensive patterns

Strategy: try-catch

Validate before calling

if (variant.getType() != Variant.Type.TIMESTAMP_MICROS
    && variant.getType() != Variant.Type.TIMESTAMP_NANOS) { /* not a timestamp variant */ }

Type guard

boolean isTimestampVariant(Variant v) {
  return v.getType() == Variant.Type.TIMESTAMP_MICROS || v.getType() == Variant.Type.TIMESTAMP_NANOS;
}

Try / catch

try { return row.getTimestamp(pos, precision); }
catch (UnsupportedOperationException e) { /* epoch long in column: construct TimestampData manually */ }

Prevention

When it happens

Trigger: Calling getTimestamp on a VariantRowDataWrapper column holding a non-timestamp variant, or elementValue resolving a TIMESTAMP-typed field whose variant is e.g. a string or long without timestamp variant type.

Common situations: Writers storing epoch longs or ISO strings in variant columns while the Flink schema declares TIMESTAMP; table schema evolution from long to timestamp.

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