apache/seatunnel · error · java.lang.IllegalArgumentException
Unable to convert to LocalTime from unexpected value '' of…
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.
Solutions
- Inspect the class name in the message; add the corresponding branch or pre-convert (Long micros -> LocalTime.ofNanoOfDay(micros*1000), Timestamp -> toLocalDateTime().toLocalTime())
- Configure Debezium time converters so TIME columns are delivered in a supported representation
- Align connector-cdc-base and connector-cdc versions (or upgrade SeaTunnel) so all Debezium temporal types are handled
- 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
- Unwrap Kafka Connect Struct fields before calling conversion utilities
- Register Debezium converters (converters=...) for TIME/TIMETZ columns
- Handle io.debezium.time.MicroTime/NanoTime Long values explicitly
- Keep connector-cdc-base and Debezium connector versions aligned
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
- Time values must use number of milliseconds greater than 0…
- Unable to convert to LocalDate from a java.sql.Date value '
- Cannot find plugin jar for pluginIdentifier
- COMMON-17
- Plugin instance: is not supported.
AI-assisted analysis of apache/seatunnel@cf67b549a7 (2026-09-10).
Data as JSON: /api/errors/a1665414771b238b.
Report an issue: GitHub.
Appendix: 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)