apache/iceberg · error · IllegalArgumentException
Cannot convert Number to variant (unknown type)
Error message
Cannot convert Number to variant (unknown type): ${number.getClass().getName()} What it means
The Number-to-Variant conversion in RecordConverter throws this IllegalArgumentException when a Number is neither Byte, Short, Integer, Long, Float, Double, BigDecimal, nor BigInteger — i.e. an unknown Number subtype the converter doesn't map to a Variant primitive.
Solutions
- Convert the value to a standard type (Integer, Long, Double, BigDecimal) before the sink
- Normalize exotic Numbers with an SMT (e.g. toLong/toDouble)
- Extend RecordConverter and override objectToVariantValue to handle the specific Number subclass
- Fix the deserializer to emit standard Java numeric boxed types
Example fix
// before value = java.util.concurrent.atomic.AtomicLong(42) // unknown Number type // after value = atomicLong.longValue() // plain Long supported by Variants.of
Defensive patterns
Strategy: type-guard
Validate before calling
static boolean isStandardNumber(Object v) {
return v instanceof Byte || v instanceof Short || v instanceof Integer || v instanceof Long
|| v instanceof Float || v instanceof Double
|| v instanceof java.math.BigDecimal || v instanceof java.math.BigInteger;
} Type guard
static Number toStandardNumber(Number n) {
if (isStandardNumber(n)) return n;
return java.math.BigDecimal.valueOf(n.doubleValue()); // normalize exotic Number
} Try / catch
try {
converter.convertValue(num, Types.VariantType.get());
} catch (IllegalArgumentException e) {
if (e.getMessage().startsWith("Cannot convert Number to variant")) {
log.error("Unknown Number subclass: " + e.getMessage());
// retry with ((Number) value).longValue() or new BigDecimal(value.toString())
}
} Prevention
- Avoid Atomic*/custom Number subclasses in Connect records
- Box to standard types at deserialization time
- Normalize exotic numerics in an SMT before the sink
- Test deserializers for the exact boxed types they emit
When it happens
Trigger: A custom Number subclass (e.g. AtomicLong, AtomicDouble, Guava/Kotlin numeric types, or a framework-specific numeric wrapper) reaches the Number branch of objectToVariantValue.
Common situations: SMTs that emit Atomic* types; deserializers producing exotic numeric wrappers; custom value classes extending Number passed through schemaless records.
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
- Cannot convert to variant
- Avro writer does not support variant types
- Cannot convert java.util.Date to variant without a…
- Cannot convert map to variant: keys must be non-null…
- Cannot extract value from type
AI-assisted analysis of apache/iceberg@86d9c8fc54 (2026-09-12).
Data as JSON: /api/errors/257ba807548f512d.
Report an issue: GitHub.
Appendix: source
Thrown at kafka-connect/kafka-connect/src/main/java/org/apache/iceberg/connect/data/RecordConverter.java:802
if (number instanceof Integer integer) {
return Variants.of(integer);
}
if (number instanceof Long longValue) {
return Variants.of(longValue);
}
if (number instanceof Float floatValue) {
return Variants.of(floatValue);
}
if (number instanceof Double doubleValue) {
return Variants.of(doubleValue);
}
if (number instanceof Byte byteValue) {
return Variants.of(byteValue);
}
if (number instanceof Short shortValue) {
return Variants.of(shortValue);
}
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(View on GitHub (pinned to 86d9c8fc54)