apache/iceberg · error · ConnectException
Cannot convert timestamptz
Error message
Cannot convert timestamptz: ${value}, type: ${value.getClass()} What it means
convertOffsetDateTime (the timestamptz conversion path in RecordConverter) throws this ConnectException when a value for an Iceberg timestamptz column is not a Number (epoch micros/millis), String (parseable timestamp), OffsetDateTime, LocalDateTime, or java.util.Date. The message includes both the value and its concrete class to aid diagnosis.
Solutions
- Send timestamps as epoch-millis Numbers, ISO-8601 strings, or use Connect's Timestamp logical type (java.util.Date)
- Use TimestampConverter SMT to normalize the field before the sink
- Log the value/class from the message and correct the producer's serialization
- If the column should hold a different type, adjust the Iceberg schema or field mapping
Example fix
// before
value = {"date":"2024-01-01","time":"10:00:00"} // nested object
// after
value = 1704112800000L // epoch millis, or "2024-01-01T10:00:00+00:00" string Defensive patterns
Strategy: validation
Validate before calling
Object v = record.value().get("tsField");
boolean ok = v == null || v instanceof Number || v instanceof String || v instanceof OffsetDateTime
|| v instanceof LocalDateTime || v instanceof java.util.Date;
if (!ok) throw new IllegalStateException("timestamptz field bad type: " + v.getClass()); Type guard
static boolean isTimestamptzCompatible(Object v) {
return v == null || v instanceof Number || v instanceof String || v instanceof OffsetDateTime
|| v instanceof LocalDateTime || v instanceof java.util.Date;
} Try / catch
try {
converter.convertValue(value, Types.TimestampType.withZone());
} catch (ConnectException e) {
if (e.getMessage().startsWith("Cannot convert timestamptz:")) {
log.error("Bad timestamptz value: " + e.getMessage());
// DLQ the record or normalize with TimestampConverter before retry
}
} Prevention
- Emit timestamps as epoch millis or ISO-8601 strings with explicit offset
- Use Connect Timestamp logical type for java.util.Date values
- Run TimestampConverter SMT to normalize formats
- Pin timestamp format in producer contracts and validate in CI
When it happens
Trigger: A timestamptz field receives a struct/map/boolean or an unparseable string via convertValue/ts; recursion hits an object type with no conversion branch.
Common situations: Producer sends nested JSON timestamp objects; ISO strings with offsets the parser rejects after ensureTimestampFormat normalization; schemaless records delivering unexpected wrappers; SMTs converting timestamps to non-temporal types.
Related errors
- Cannot convert timestamp
- Cannot convert date
- Cannot convert time
- Cannot convert to binary
- Cannot convert to double
AI-assisted analysis of apache/iceberg@86d9c8fc54 (2026-09-12).
Data as JSON: /api/errors/fd6e00a1d79d709d.
Report an issue: GitHub.
Appendix: source
Thrown at kafka-connect/kafka-connect/src/main/java/org/apache/iceberg/connect/data/RecordConverter.java:820
throw new IllegalArgumentException(
"Cannot convert Number to variant (unknown type): " + number.getClass().getName());
}
@SuppressWarnings("JavaUtilDate")
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) {
return ((LocalDateTime) value).atOffset(ZoneOffset.UTC);
} else if (value instanceof Date) {
return DateTimeUtil.timestamptzFromMicros(((Date) value).getTime() * 1000);
}
throw new ConnectException(
"Cannot convert timestamptz: " + value + ", type: " + value.getClass());
}
private OffsetDateTime parseOffsetDateTime(String str) {
String tsStr = ensureTimestampFormat(str);
try {
return OFFSET_TIMESTAMP_FORMAT.parse(tsStr, OffsetDateTime::from);
} catch (DateTimeParseException e) {
return LocalDateTime.parse(tsStr, DateTimeFormatter.ISO_LOCAL_DATE_TIME)
.atOffset(ZoneOffset.UTC);
}
}
@SuppressWarnings("JavaUtilDate")
private LocalDateTime convertLocalDateTime(Object value) {
if (value instanceof Number) {
long millis = ((Number) value).longValue();
return DateTimeUtil.timestampFromMicros(millis * 1000);View on GitHub (pinned to 86d9c8fc54)