apache/iceberg · error · IllegalArgumentException

Cannot convert to variant

Error message

Cannot convert to variant: ${value.getClass().getName()}

What it means

objectToVariantValue throws this IllegalArgumentException when a value destined for an Iceberg Variant column is of an unrecognized Java type. Supported inputs are primitives, CharSequence, byte arrays/ByteBuffer, BigDecimal, temporal types, Struct, and Map; anything else (or null-typed containers it cannot interpret) fails.

Solutions

  1. Pre-transform the value into a supported type (String, Number, byte[], Map, Struct) in an SMT
  2. Log the offending class from the message and add an explicit conversion branch in a RecordConverter subclass
  3. Fix the producer to emit JSON/Avro values that map to standard Connect types
  4. If the field is not meant to be a Variant, correct the table schema or field mapping

Example fix

// before
value = new com.fasterxml.jackson.databind.JsonNode(...) // unsupported class
// after
value = jsonNode.toString() // or convert to Map<String,Object> first
Defensive patterns

Strategy: type-guard

Validate before calling

static boolean isVariantCompatible(Object v) {
  return v == null || v instanceof Boolean || v instanceof Number || v instanceof CharSequence
      || v instanceof byte[] || v instanceof ByteBuffer || v instanceof java.math.BigDecimal
      || v instanceof java.time.temporal.Temporal || v instanceof Map || v instanceof Struct;
}

Type guard

static void requireVariantCompatible(Object v) {
  if (v != null && !isVariantCompatible(v)) {
    throw new IllegalStateException("Unsupported variant type: " + v.getClass());
  }
}

Try / catch

try {
  converter.convertValue(value, Types.VariantType.get());
} catch (IllegalArgumentException e) {
  if (e.getMessage().startsWith("Cannot convert to variant:")) {
    log.error("Unsupported variant input class", e);
    // fallback: serialize value to JSON string and store as string variant
  }
}

Prevention

When it happens

Trigger: A Variant field receives a custom domain object, an array/List not wrapped as expected, or some type produced by an SMT that the converter has no branch for.

Common situations: Custom SMTs output exotic types (e.g. Guava objects, custom Avro wrappers); schema-less JSON arrives as JsonNode-like objects; version upgrade introduced a new Variant conversion path the payload doesn't match.

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/iceberg@86d9c8fc54 (2026-09-12). Data as JSON: /api/errors/b728dd35af49169f. Report an issue: GitHub.

Appendix: source

Thrown at kafka-connect/kafka-connect/src/main/java/org/apache/iceberg/connect/data/RecordConverter.java:670

      ValueArray array = Variants.array();
      org.apache.kafka.connect.data.Schema elementSchema =
          schema != null ? schema.valueSchema() : null;
      for (Object element : collection) {
        array.add(objectToVariantValue(element, metadata, elementSchema));
      }
      return array;
    }
    if (value instanceof Map<?, ?> map) {
      return mapToVariantValue(map, metadata, schema);
    }
    if (value instanceof Struct struct) {
      ShreddedObject object = Variants.object(metadata);
      for (Field field : struct.schema().fields()) {
        object.put(field.name(), objectToVariantValue(struct.get(field), metadata, field.schema()));
      }
      return object;
    }
    throw new IllegalArgumentException("Cannot convert to variant: " + value.getClass().getName());
  }

  /** Converts a Map to VariantValue; throw IllegalArgumentException if the key is not a string. */
  private static VariantValue mapToVariantValue(
      Map<?, ?> map, VariantMetadata metadata, org.apache.kafka.connect.data.Schema schema) {
    ShreddedObject object = Variants.object(metadata);
    org.apache.kafka.connect.data.Schema mapValueSchema =
        schema != null ? schema.valueSchema() : null;
    map.forEach(
        (key, val) -> {
          if (key instanceof String keyStr) {
            object.put(keyStr, objectToVariantValue(val, metadata, mapValueSchema));
          } else {
            throw new IllegalArgumentException(
                "Cannot convert map to variant: keys must be non-null strings, was: "
                    + (key == null ? "null" : key.getClass().getName()));
          }
        });

View on GitHub (pinned to 86d9c8fc54)