apache/seatunnel · error · IllegalArgumentException

Unable to convert to LocalDateTime from unexpected value '${

Error message

Unable to convert to LocalDateTime from unexpected value '${value}' of type ${value.getClass().getName()}

What it means

convertToTimestamp converts TIMESTAMP columns to LocalDateTime; its switch covers known input types and the default branch rejects any other class with an IllegalArgumentException naming the value and its class name.

Source

Thrown at seatunnel-connectors-v2/connector-cdc/connector-cdc-tidb/src/main/java/org/apache/seatunnel/connectors/seatunnel/cdc/tidb/source/converter/DefaultDataConverter.java:333

                    int nanoSecond = instant.getNano();
                    long millisecond = epochSecond * 1000L + (long) (nanoSecond / 1000000);
                    int nanoOfMillisecond = nanoSecond % 1000000;
                    return toLocalDateTime(millisecond, nanoOfMillisecond);
                }
                break;
            case TypeDatetime:
                if (value instanceof Timestamp) {
                    LocalDateTime dateTime = ((Timestamp) value).toLocalDateTime();
                    long epochDay = dateTime.toLocalDate().toEpochDay();
                    long nanoOfDay = dateTime.toLocalTime().toNanoOfDay();
                    long millisecond = epochDay * 86400000L + nanoOfDay / 1000000L;
                    int nanoOfMillisecond = (int) (nanoOfDay % 1000000L);

                    return toLocalDateTime(millisecond, nanoOfMillisecond);
                }
                break;
            default:
                throw new IllegalArgumentException(
                        "Unable to convert to LocalDateTime from unexpected value '"
                                + value
                                + "' of type "
                                + value.getClass().getName());
        }
        return value;
    }

    public static LocalDateTime toLocalDateTime(long millisecond, int nanoOfMillisecond) {
        // 86400000 = 24 * 60 * 60 * 1000
        int date = (int) (millisecond / 86400000);
        int time = (int) (millisecond % 86400000);
        if (time < 0) {
            --date;
            time += 86400000;
        }
        long nanoOfDay = time * 1_000_000L + nanoOfMillisecond;
        LocalDate localDate = LocalDate.ofEpochDay(date);

View on GitHub (pinned to cf67b549a7)

Solutions

  1. Identify the actual runtime class from the message and add a conversion branch for it in convertToTimestamp
  2. Parse Strings with the expected TiDB datetime pattern into LocalDateTime explicitly
  3. Ensure schema types match the real TiDB column types so only temporal classes arrive
  4. Align SeaTunnel connector-cdc-tidb with the TiDB CDC version in use

Example fix

// before
default:
    throw new IllegalArgumentException("Unable to convert to LocalDateTime...");
// after
case String:
    return LocalDateTime.parse(((String) value).replace(' ', 'T'));
default:
    throw new IllegalArgumentException("Unable to convert to LocalDateTime...");
Defensive patterns

Strategy: type-guard

Validate before calling

static boolean isLocalDateTimeConvertible(Object v) {
    return v instanceof LocalDateTime || v instanceof java.sql.Timestamp
        || v instanceof Long || v instanceof String;
}

Type guard

static LocalDateTime toLdtSafe(Object v) {
    if (v instanceof LocalDateTime) return (LocalDateTime) v;
    if (v instanceof java.sql.Timestamp) return ((java.sql.Timestamp) v).toLocalDateTime();
    if (v instanceof Long) return Instant.ofEpochMilli((Long) v).atZone(ZoneId.systemDefault()).toLocalDateTime();
    if (v instanceof String) return LocalDateTime.parse(((String) v).replace(' ', 'T'));
    return null;
}

Try / catch

try {
    return convertToTimestamp(value, dataType);
} catch (IllegalArgumentException e) {
    log.error("TIMESTAMP conversion failed for {} : {}", value.getClass(), e.getMessage());
    throw e;
}

Prevention

When it happens

Trigger: A TIMESTAMP column whose runtime value is outside the handled types — e.g. String, byte[], or Integer epoch — falling into the switch's default branch during convert().

Common situations: TiDB CDC emitting TIMESTAMP as String after an upgrade; schema drift mapping a non-temporal column to TIMESTAMP; wrapper objects from upstream transforms.

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