apache/seatunnel · error · java.lang.IllegalArgumentException

Unable to convert to LocalTime from unexpected value '' of t

Error message

Unable to convert to LocalTime from unexpected value '' of type 

What it means

toLocalTime supports LocalTime, LocalDateTime, java.sql.Time, Duration, and String (TIMETZ parsed with a timezone formatter). Any other object type reaches the terminal throw, which reports the value and its class. It indicates the deserializer produced a time representation this utility does not recognize.

Source

Thrown at seatunnel-connectors-v2/connector-cdc/connector-cdc-base/src/main/java/org/apache/seatunnel/connectors/cdc/debezium/utils/TemporalConversions.java:151

        }
        if (obj instanceof Duration) {
            Long value = ((Duration) obj).toNanos();
            if (value >= 0 && value <= NANOSECONDS_PER_DAY) {
                return LocalTime.ofNanoOfDay(value);
            } else {
                throw new IllegalArgumentException(
                        "Time values must use number of milliseconds greater than 0 and less than 86400000000000");
            }
        }
        if (obj instanceof String) {
            // The TIMETZ column is returned as a String which we initially parse here
            // The parsed offset-time potentially has a zone-offset from the data, shift it after to
            // GMT.
            final OffsetTime offsetTime =
                    OffsetTime.parse((String) obj, TIME_WITH_TIMEZONE_FORMATTER);
            return offsetTime.toLocalTime();
        }
        throw new IllegalArgumentException(
                "Unable to convert to LocalTime from unexpected value '"
                        + obj
                        + "' of type "
                        + obj.getClass().getName());
    }

    @SuppressWarnings("MagicNumber")
    public static LocalDateTime toLocalDateTime(Object obj, ZoneId serverTimeZone) {
        if (obj == null) {
            return null;
        }
        if (obj instanceof OffsetDateTime) {
            return ((OffsetDateTime) obj).toLocalDateTime();
        }
        if (obj instanceof Instant) {
            return ((Instant) obj).atOffset(ZoneOffset.UTC).toLocalDateTime();
        }
        if (obj instanceof LocalDateTime) {

View on GitHub (pinned to cf67b549a7)

Solutions

  1. Inspect the class name in the message; add the corresponding branch or pre-convert (Long micros -> LocalTime.ofNanoOfDay(micros*1000), Timestamp -> toLocalDateTime().toLocalTime())
  2. Configure Debezium time converters so TIME columns are delivered in a supported representation
  3. Align connector-cdc-base and connector-cdc versions (or upgrade SeaTunnel) so all Debezium temporal types are handled
  4. Unwrap Debezium structs before conversion (value without schema: org.apache.kafka.connect.data.Struct -> field value)

Example fix

// before
Object v = struct.get("start_time"); // Long micros from Debezium MicroTime
LocalTime t = TemporalConversions.toLocalTime(v); // throws
// after
LocalTime t = (v instanceof Long)
    ? LocalTime.ofNanoOfDay((Long) v * 1000)
    : TemporalConversions.toLocalTime(v);
Defensive patterns

Strategy: type-guard

Validate before calling

boolean isSupportedTimeType(Object v) {
    return v instanceof LocalTime || v instanceof LocalDateTime
        || v instanceof java.sql.Time || v instanceof Duration
        || v instanceof String;
}

Type guard

static LocalTime safeToLocalTime(Object v) {
    if (v instanceof Long) return LocalTime.ofNanoOfDay((Long) v * 1000);
    if (v instanceof java.sql.Timestamp) return ((java.sql.Timestamp) v).toLocalDateTime().toLocalTime();
    return TemporalConversions.toLocalTime(v);
}

Try / catch

try {
    LocalTime t = TemporalConversions.toLocalTime(value);
} catch (IllegalArgumentException e) {
    LOG.error("Unsupported TIME type {}: {}", value.getClass(), value, e);
    throw e;
}

Prevention

When it happens

Trigger: Calling toLocalTime/localTime with an object of an unsupported class — e.g. java.sql.Timestamp, io.debezium.time.MicroTime as a raw Long, OffsetTime already parsed, or a byte-array/binary representation from a TIME column.

Common situations: Debezium emitting io.debezium.time.Time/MicroTime/NanoTime numeric values because no converter was configured; upstream schema changes adding a new wire type; custom SMTs transforming time columns into Timestamp; version drift between connector-cdc-base and the Debezium connector.

Understand the failure class

Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.

Related errors


AI-assisted analysis of apache/seatunnel@cf67b549a7 (2026-09-10). Data as JSON: /api/errors/a1665414771b238b. Report an issue: GitHub.