apache/iceberg · error · UnsupportedOperationException

Not a supported type:

Error message

Not a supported type: 

What it means

SparkValueConverter.convert maps Iceberg primitive types to Spark-compatible values and throws UnsupportedOperationException for any type it does not explicitly handle. It is a deliberate capability guard: the converter only supports a fixed set of primitive types (the visible cases pass through DOUBLE/DECIMAL/STRING/FIXED values unchanged), so encountering an unhandled typeId means the requested conversion is outside the converter's supported surface.

Solutions

  1. Check the Iceberg type of the offending column and confirm the converter version handles it; upgrade to an Iceberg/Spark version where the type is supported
  2. Convert unsupported types (e.g. timestamps, binary) manually before calling the converter
  3. If the type should be supported, upgrade the iceberg-spark module or file an issue to extend the switch

Example fix

// before
Object sparkVal = SparkValueConverter.convert(structType, row); // throws for TIMESTAMP field
// after
Object raw = row.get(tsFieldPos);
Object sparkVal = raw instanceof Long
    ? DateTimeUtils.microsToTimestamp((Long) raw)
    : SparkValueConverter.convert(structType, row);
Defensive patterns

Strategy: type-guard

Validate before calling

Set<Type.TypeID> supported = Set.of(Type.TypeID.BOOLEAN, Type.TypeID.INTEGER, Type.TypeID.LONG,
    Type.TypeID.FLOAT, Type.TypeID.DOUBLE, Type.TypeID.DECIMAL, Type.TypeID.STRING, Type.TypeID.FIXED);
if (!supported.contains(type.typeId())) {
  throw new IllegalArgumentException("Column type not supported by converter: " + type);
}

Type guard

boolean isSupportedPrimitive(Type t) {
  switch (t.typeId()) {
    case BOOLEAN: case INTEGER: case LONG: case FLOAT: case DOUBLE:
    case DECIMAL: case STRING: case FIXED:
      return true;
    default:
      return false;
  }
}

Try / catch

try {
  value = SparkValueConverter.convert(structType, row);
} catch (UnsupportedOperationException e) {
  LOG.warn("Unsupported type conversion, passing raw value through", e);
  value = row.getField(fieldName);
}

Prevention

When it happens

Trigger: Calling the public convert method with an Iceberg type whose typeId falls into the switch's default branch - e.g. a TIMESTAMP, TIMESTAMP_NS, DATE, UUID, or BINARY column passed through this conversion path.

Common situations: Reading or writing tables containing timestamp, binary, or uuid columns via Spark code paths that use SparkValueConverter for row conversion; using newer Iceberg type kinds (timestamp-nanoseconds) with an older Spark integration module.

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

Appendix: source

Thrown at spark/v4.1/spark/src/main/java/org/apache/iceberg/spark/SparkValueConverter.java:90

        // if spark.sql.datetime.java8API.enabled is set to true, java.time.LocalDate
        // for Spark SQL DATE type otherwise java.sql.Date is returned.
        return DateTimeUtils.anyToDays(object);
      case TIMESTAMP:
        return DateTimeUtils.anyToMicros(object);
      case BINARY:
        return ByteBuffer.wrap((byte[]) object);
      case INTEGER:
        return ((Number) object).intValue();
      case BOOLEAN:
      case LONG:
      case FLOAT:
      case DOUBLE:
      case DECIMAL:
      case STRING:
      case FIXED:
        return object;
      default:
        throw new UnsupportedOperationException("Not a supported type: " + type);
    }
  }

  private static Record convert(Types.StructType struct, Row row) {
    if (row == null) {
      return null;
    }

    Record record = GenericRecord.create(struct);
    List<Types.NestedField> fields = struct.fields();
    for (int i = 0; i < fields.size(); i += 1) {
      Types.NestedField field = fields.get(i);

      Type fieldType = field.type();

      switch (fieldType.typeId()) {
        case STRUCT:
          record.set(i, convert(fieldType.asStructType(), row.getStruct(i)));

View on GitHub (pinned to 86d9c8fc54)