apache/iceberg · error · UnsupportedOperationException

Cannot extract value from type

Error message

Cannot extract value from type: ${recordValue.getClass().getName()}

What it means

RecordUtils.extractFromRecordValue only supports Kafka Connect Struct and Map record values when resolving a dotted fieldName (e.g. upsert key columns or partition/route columns). Any other type throws UnsupportedOperationException naming the actual class.

Solutions

  1. Ensure the record value schema is a Struct (use JsonConverter with schemas.enable=true or an SMT like HoistField to wrap primitives into a struct).
  2. If values are JSON strings, add a transform that parses JSON into a Struct before the sink.
  3. Change the configured field reference to point at a field that exists in a Struct/Map value.
  4. Use IcebergSinkConfig route/key settings matching the actual record shape.

Example fix

// before: value schema is int64, config expects 'user.id'
"iceberg.route-field": "user.id"
// after: wrap primitive into struct via HoistField then reference
"transforms.hoist.type": "org.apache.kafka.connect.transforms.HoistField$Value",
"transforms.hoist.field": "user"
Defensive patterns

Strategy: type-guard

Validate before calling

if (!(record.value() instanceof Struct) && !(record.value() instanceof Map)) {
  throw new DataException("Field extraction requires Struct or Map value");
}

Type guard

boolean isExtractable(Object v) {
  return v instanceof Struct || v instanceof Map;
}

Try / catch

try { return RecordUtils.extractFromRecordValue(value, fieldName); }
catch (UnsupportedOperationException e) { LOG.error("non-struct value: {}", e.getMessage()); throw new RetriableException(e); }

Prevention

When it happens

Trigger: Configuring iceberg.coordinator.key-field / partition-field / route-field (via the sink's field extraction) while the record value is a raw primitive, byte[], or other non-Struct/non-Map Connect type.

Common situations: Topics with schema-less or primitive values (string/JSON-as-string) paired with a config expecting nested field lookups; converters producing ByteArray/Json schemas for keys.

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

Appendix: source

Thrown at kafka-connect/kafka-connect/src/main/java/org/apache/iceberg/connect/data/RecordUtils.java:58

import org.apache.iceberg.relocated.com.google.common.collect.Sets;
import org.apache.iceberg.relocated.com.google.common.primitives.Ints;
import org.apache.iceberg.types.TypeUtil;
import org.apache.iceberg.types.Types.NestedField;
import org.apache.iceberg.util.PropertyUtil;
import org.apache.kafka.connect.data.Field;
import org.apache.kafka.connect.data.Struct;

class RecordUtils {

  @SuppressWarnings("unchecked")
  static Object extractFromRecordValue(Object recordValue, String fieldName) {
    List<String> fields = Splitter.on('.').splitToList(fieldName);
    if (recordValue instanceof Struct) {
      return valueFromStruct((Struct) recordValue, fields);
    } else if (recordValue instanceof Map) {
      return valueFromMap((Map<String, ?>) recordValue, fields);
    } else {
      throw new UnsupportedOperationException(
          "Cannot extract value from type: " + recordValue.getClass().getName());
    }
  }

  private static Object valueFromStruct(Struct parent, List<String> fields) {
    Struct struct = parent;
    for (int idx = 0; idx < fields.size() - 1; idx++) {
      Object value = fieldValueFromStruct(struct, fields.get(idx));
      if (value == null) {
        return null;
      }
      Preconditions.checkState(value instanceof Struct, "Expected a struct type");
      struct = (Struct) value;
    }
    return fieldValueFromStruct(struct, fields.get(fields.size() - 1));
  }

  private static Object fieldValueFromStruct(Struct struct, String fieldName) {

View on GitHub (pinned to 86d9c8fc54)