apache/seatunnel · error · IllegalArgumentException

Unsupported value type for TIMESTAMP_TZ conversion: ${value.

Error message

Unsupported value type for TIMESTAMP_TZ conversion: ${value.getClass().getName()}, value='${value}', TiDB dataType=${dataType}

What it means

When convertToOffsetDateTime receives a TIMESTAMP_TZ value whose type is neither OffsetDateTime, LocalDateTime, Timestamp-like, nor String, it throws an IllegalArgumentException describing the class name, value, and TiDB data type, since no conversion path exists.

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:299

            // LocalDateTime without explicit zone — treat as UTC wall-clock value.
            return ((LocalDateTime) value).atOffset(ZoneOffset.UTC);
        }
        if (value instanceof String) {
            // String representation from TiDB CDC — attempt ISO-8601 parse.
            try {
                return OffsetDateTime.parse((String) value);
            } catch (java.time.format.DateTimeParseException e) {
                throw new IllegalArgumentException(
                        "Unable to convert TIMESTAMP_TZ from String value: '"
                                + value
                                + "' for TiDB dataType: "
                                + dataType,
                        e);
            }
        }
        // Unknown type — fail fast with enough context for diagnosis instead of silently
        // returning the raw value which would cause a ClassCastException downstream.
        throw new IllegalArgumentException(
                "Unsupported value type for TIMESTAMP_TZ conversion: "
                        + value.getClass().getName()
                        + ", value='"
                        + value
                        + "', TiDB dataType="
                        + dataType);
    }

    private static Object convertToTimestamp(
            Object value, org.tikv.common.types.DataType dataType) {
        switch (dataType.getType()) {
            case TypeTimestamp:
                if (value instanceof Timestamp) {
                    Instant instant = ((Timestamp) value).toInstant();
                    long epochSecond = instant.getEpochSecond();
                    int nanoSecond = instant.getNano();
                    long millisecond = epochSecond * 1000L + (long) (nanoSecond / 1000000);
                    int nanoOfMillisecond = nanoSecond % 1000000;

View on GitHub (pinned to cf67b549a7)

Solutions

  1. Log/check value.getClass().getName() in the message to identify the actual emitted type
  2. Add a conversion branch for that type (e.g. epoch millis via Instant.ofEpochMilli(...).atZone(...).toOffsetDateTime())
  3. Align connector and TiDB CDC plugin versions so expected classes are emitted
  4. Correct the schema/type mapping so only genuine timestamp values reach TIMESTAMP_TZ conversion

Example fix

// before
throw new IllegalArgumentException("Unsupported value type...");
// after
if (value instanceof Long) {
    return Instant.ofEpochMilli((Long) value).atZone(ZoneId.of(timezone)).toOffsetDateTime();
}
throw new IllegalArgumentException("Unsupported value type...");
Defensive patterns

Strategy: type-guard

Validate before calling

static boolean isTimestampConvertible(Object v) {
    return v instanceof OffsetDateTime || v instanceof LocalDateTime
        || v instanceof String || v instanceof Long || v instanceof java.util.Date;
}

Type guard

static OffsetDateTime toOdtSafe(Object v, ZoneId zone) {
    if (v instanceof OffsetDateTime) return (OffsetDateTime) v;
    if (v instanceof LocalDateTime) return ((LocalDateTime) v).atZone(zone).toOffsetDateTime();
    if (v instanceof Long) return Instant.ofEpochMilli((Long) v).atZone(zone).toOffsetDateTime();
    if (v instanceof String) return OffsetDateTime.parse(((String) v).replace(' ', 'T'));
    return null;
}

Try / catch

try {
    return convertToOffsetDateTime(value, dataType);
} catch (IllegalArgumentException e) {
    // message already includes class name; route to DLQ or rethrow
    throw e;
}

Prevention

When it happens

Trigger: convert() passes an unexpected object for a TIMESTAMP_TZ column — e.g. Long epoch millis, java.util.Date, or byte[] — after the String/known-type branches did not match.

Common situations: TiDB CDC version change altering emitted classes; schema mismatched so an INT/epoch column mapped to TIMESTAMP_TZ; upstream transform wrapping values.

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