apache/seatunnel · error · RuntimeException

Cannot convert time:

Error message

Cannot convert time: 

What it means

Iceberg RowConverter throws this RuntimeException when a time column value is not a Number (millis/micros), LocalTime, or java.util.Date. convertTimeValue supports only those types; anything else is rejected with the raw value in the message.

Source

Thrown at seatunnel-connectors-v2/connector-iceberg/src/main/java/org/apache/seatunnel/connectors/seatunnel/iceberg/data/RowConverter.java:414

            int days = (int) (((Date) value).getTime() / 1000 / 60 / 60 / 24);
            return DateTimeUtil.dateFromDays(days);
        }
        throw new RuntimeException("Cannot convert date: " + value);
    }

    protected LocalTime convertTimeValue(Object value) {
        if (value instanceof Number) {
            long millis = ((Number) value).longValue();
            return DateTimeUtil.timeFromMicros(millis * 1000);
        } else if (value instanceof String) {
            return LocalTime.parse((String) value);
        } else if (value instanceof LocalTime) {
            return (LocalTime) value;
        } else if (value instanceof Date) {
            long millis = ((Date) value).getTime();
            return DateTimeUtil.timeFromMicros(millis * 1000);
        }
        throw new RuntimeException("Cannot convert time: " + value);
    }

    protected Temporal convertTimestampValue(Object value, Types.TimestampType type) {
        if (type.shouldAdjustToUTC()) {
            return convertOffsetDateTime(value);
        }
        return convertLocalDateTime(value);
    }

    private OffsetDateTime convertOffsetDateTime(Object value) {
        if (value instanceof Number) {
            long millis = ((Number) value).longValue();
            return DateTimeUtil.timestamptzFromMicros(millis * 1000);
        } else if (value instanceof String) {
            return parseOffsetDateTime((String) value);
        } else if (value instanceof OffsetDateTime) {
            return (OffsetDateTime) value;
        } else if (value instanceof LocalDateTime) {

View on GitHub (pinned to cf67b549a7)

Solutions

  1. Convert the value to LocalTime before the sink.
  2. Add a transform CAST(column, TIME) so the sink receives a supported type.
  3. Parse string times upstream: LocalTime.parse(str).

Example fix

// before
writer.write(idx, "10:30:00"); // String not accepted
// after
writer.write(idx, LocalTime.parse("10:30:00")); // LocalTime accepted
Defensive patterns

Strategy: type-guard

Validate before calling

if (!(v instanceof Number) && !(v instanceof LocalTime) && !(v instanceof java.util.Date)) throw new IllegalArgumentException("time column must be Number/LocalTime/Date, got " + (v == null ? "null" : v.getClass().getName()));

Type guard

boolean isTimeConvertible(Object v) { return v instanceof Number || v instanceof LocalTime || v instanceof java.util.Date; }

Try / catch

try { sink.write(row); } catch (RuntimeException e) { if (e.getMessage() != null && e.getMessage().startsWith("Cannot convert time")) { /* parse string or DLQ */ } else throw e; }

Prevention

When it happens

Trigger: convertTimeValue receives e.g. a String '10:30:00', OffsetDateTime, or Duration while mapping to an Iceberg TIME column.

Common situations: String-formatted times from text-based sources (CSV/JSON); sources that encode time as OffsetDateTime; schema mismatch after upstream type changes.

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/seatunnel@cf67b549a7 (2026-09-10). Data as JSON: /api/errors/cc4fafd1a1d141ce. Report an issue: GitHub.