apache/iceberg · error · UnsupportedOperationException

Not a supported type: ${targetType}

Error message

Not a supported type: ${targetType}

What it means

DataConverter.get() builds a converter for pairs of Flink logical types but only supports RowType, ArrayType, and MapType at the top level. Any other LogicalType (e.g. raw VARBINARY used here, or unsupported roots) hits the default branch and raises UnsupportedOperationException.

Source

Thrown at flink/v2.1/flink/src/main/java/org/apache/iceberg/flink/sink/dynamic/DataConverter.java:127

        };
      case TIMESTAMP_WITHOUT_TIME_ZONE:
        return object -> {
          if (object instanceof Integer) {
            LocalDateTime dateTime =
                LocalDateTime.of(LocalDate.ofEpochDay((Integer) object), LocalTime.MIN);
            return TimestampData.fromLocalDateTime(dateTime);
          } else {
            return object;
          }
        };
      case ROW:
        return new RowDataConverter((RowType) sourceType, (RowType) targetType);
      case ARRAY:
        return new ArrayConverter((ArrayType) sourceType, (ArrayType) targetType);
      case MAP:
        return new MapConverter((MapType) sourceType, (MapType) targetType);
      default:
        throw new UnsupportedOperationException("Not a supported type: " + targetType);
    }
  }

  static DataConverter nullable(DataConverter converter) {
    return value -> value == null ? null : converter.convert(value);
  }

  class RowDataConverter implements DataConverter {
    private final RowData.FieldGetter[] fieldGetters;
    private final DataConverter[] dataConverters;

    RowDataConverter(RowType sourceType, RowType targetType) {
      this.fieldGetters = new RowData.FieldGetter[targetType.getFields().size()];
      this.dataConverters = new DataConverter[targetType.getFields().size()];

      for (int i = 0; i < targetType.getFields().size(); i++) {
        RowData.FieldGetter fieldGetter;
        DataConverter dataConverter;

View on GitHub (pinned to 86d9c8fc54)

Solutions

  1. Ensure the evolving field types are restricted to ROW, ARRAY, or MAP structures supported by the dynamic sink.
  2. If a type was evolved to an unsupported one, roll back the schema change or add an explicit case to DataConverter.get for that type.
  3. Pre-transform data so the target field uses a supported type before writing.

Example fix

// before: target field type = VARBINARY
// after: keep the field as BINARY<->BYTES compatible or wrap in a supported structure
ALTER TABLE db.t MODIFY COLUMN payload BYTES; // if BYTES<->VARBINARY handled upstream, else avoid this evolution
Defensive patterns

Strategy: validation

Validate before calling

LogicalType root = targetType;
if (!(root instanceof RowType || root instanceof ArrayType || root instanceof MapType)) {
  throw new IllegalArgumentException("Dynamic sink only supports ROW/ARRAY/MAP roots, got: " + root);
}

Type guard

static boolean isSupportedRoot(LogicalType t) {
  return t instanceof RowType || t instanceof ArrayType || t instanceof MapType;
}

Prevention

When it happens

Trigger: Calling the dynamic sink's DataConverter.get(sourceType, targetType) where targetType's root logical type is not ROW, ARRAY, or MAP — e.g. a top-level column type of BINARY/VARBINARY or a custom logical type.

Common situations: Schema evolution where a field's type changed into an unsupported category; users feeding non-row-rooted types into the dynamic sink's schema-compatibility machinery.

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