apache/iceberg · error · IllegalArgumentException

Cannot convert to binary

Error message

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

What it means

RecordConverter.convertBase64Binary throws this IllegalArgumentException when a value targeted at an Iceberg binary column is neither a Base64-encoded String, byte[], nor ByteBuffer. The converter cannot wrap the given type into the expected ByteBuffer representation.

Solutions

  1. Declare the Connect field as bytes (SchemaBuilder.bytes()) or string (Base64) so the converter can decode it
  2. Wrap the raw value in ByteBuffer or byte[] via an SMT before it reaches the sink
  3. Inspect the offending value's actual class in the message and align the Iceberg column type with it
  4. If the data is really a struct/list, change the Iceberg table column type instead of binary

Example fix

// before
Schema bad = SchemaBuilder.int64().build(); // long value -> cannot convert to binary
// after
Schema good = SchemaBuilder.bytes().build(); // byte[]/ByteBuffer accepted
Defensive patterns

Strategy: type-guard

Validate before calling

if (value != null && !(value instanceof String || value instanceof byte[] || value instanceof ByteBuffer)) {
  throw new IllegalStateException("Binary field must be Base64 String, byte[], or ByteBuffer, got: " + value.getClass());
}

Type guard

static boolean isBinaryCompatible(Object v) {
  return v == null || v instanceof String || v instanceof byte[] || v instanceof ByteBuffer;
}

Try / catch

try {
  converter.convertValue(value, Types.BinaryType.get());
} catch (IllegalArgumentException e) {
  log.error("Binary conversion failed for class: " + e.getMessage(), e);
  // DLQ record or convert via ByteBuffer.wrap(String.valueOf(value).getBytes(StandardCharsets.UTF_8))
}

Prevention

When it happens

Trigger: A binary Iceberg field receives a Number, Boolean, Map, List, or Struct from the Kafka Connect record instead of String/byte[]/ByteBuffer.

Common situations: Kafka Connect schema declares a field as int/struct while the Iceberg table expects binary; JSON-encoded binary arrives as a nested object; a schema evolution changed the field type on the producer side but not the Iceberg table (or vice versa).

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

Appendix: source

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

    if (FileFormat.PARQUET
        .name()
        .toLowerCase(Locale.ROOT)
        .equals(config.writeProps().get(TableProperties.DEFAULT_FILE_FORMAT))) {
      return UUIDUtil.convert(uuid);
    } else {
      return uuid;
    }
  }

  protected ByteBuffer convertBase64Binary(Object value) {
    if (value instanceof String) {
      return ByteBuffer.wrap(Base64.getDecoder().decode((String) value));
    } else if (value instanceof byte[]) {
      return ByteBuffer.wrap((byte[]) value);
    } else if (value instanceof ByteBuffer) {
      return (ByteBuffer) value;
    }
    throw new IllegalArgumentException("Cannot convert to binary: " + value.getClass().getName());
  }

  @SuppressWarnings("JavaUtilDate")
  protected LocalDate convertDateValue(Object value) {
    if (value instanceof Number) {
      int days = ((Number) value).intValue();
      return DateTimeUtil.dateFromDays(days);
    } else if (value instanceof String) {
      return LocalDate.parse((String) value);
    } else if (value instanceof LocalDate) {
      return (LocalDate) value;
    } else if (value instanceof Date) {
      int days = (int) (((Date) value).getTime() / 1000 / 60 / 60 / 24);
      return DateTimeUtil.dateFromDays(days);
    }
    throw new ConnectException("Cannot convert date: " + value);
  }

View on GitHub (pinned to 86d9c8fc54)