apache/iceberg · error · IllegalArgumentException

Cannot convert to string

Error message

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

What it means

convertString converts values to Iceberg StringType: it accepts String directly and Connect Struct (serialized to JSON via the configured JsonConverter); try/catch also allows other values where String.valueOf would work. If the value is neither a String nor a Struct (and the surrounding conversions don't apply), it throws IllegalArgumentException naming the class.

Solutions

  1. Emit String values for string-typed columns, or wrap structured data in a Struct so it is JSON-serialized.
  2. Change the Iceberg column type to match the actual value type (e.g. MapType or a struct).
  3. Add an SMT (e.g. ValueToKey/Cast) to coerce the value to string before the sink.
  4. Catch IllegalArgumentException and route the record to a DLQ.

Example fix

// before
record.set("meta", Map.of("k", "v")); // Map into StringType
// after
record.set("meta", "{\"k\":\"v\"}"); // pre-serialized JSON string, or use a JSON-typed/VARIANT column
Defensive patterns

Strategy: validation

Validate before calling

Object v = record.get("meta");
if (!(v instanceof String) && !(v instanceof Struct)) {
  throw new IllegalArgumentException("meta must be String or Struct, got " + v.getClass().getSimpleName());
}

Type guard

boolean isStringConvertible(Object v) {
  return v instanceof String || v instanceof Struct;
}

Try / catch

try {
  sink.write(record);
} catch (IllegalArgumentException e) {
  if (e.getMessage() != null && e.getMessage().contains("Cannot convert to string")) {
    sendToDlq(record, e);
  } else throw e;
}

Prevention

When it happens

Trigger: A field mapped to an Iceberg StringType column holding a value type not handled by the try-block coercion — e.g. a raw byte[] or Map/List that the string conversion path rejects.

Common situations: String columns receiving structured values from schemaless JSON; producers emitting binary payloads into string columns; nested maps landing in string columns after table schema drift.

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

Appendix: source

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

  }

  protected String convertString(Object value) {
    try {
      if (value instanceof String) {
        return (String) value;
      } else if (value instanceof Number || value instanceof Boolean) {
        return value.toString();
      } else if (value instanceof Map || value instanceof List) {
        return MAPPER.writeValueAsString(value);
      } else if (value instanceof Struct) {
        Struct struct = (Struct) value;
        byte[] data = config.jsonConverter().fromConnectData(null, struct.schema(), struct);
        return new String(data, StandardCharsets.UTF_8);
      }
    } catch (IOException e) {
      throw new UncheckedIOException(e);
    }
    throw new IllegalArgumentException("Cannot convert to string: " + value.getClass().getName());
  }

  protected Object convertUUID(Object value) {
    UUID uuid;
    if (value instanceof String) {
      uuid = UUID.fromString((String) value);
    } else if (value instanceof UUID) {
      uuid = (UUID) value;
    } else {
      throw new IllegalArgumentException("Cannot convert to UUID: " + value.getClass().getName());
    }

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

View on GitHub (pinned to 86d9c8fc54)