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
- Emit String values for string-typed columns, or wrap structured data in a Struct so it is JSON-serialized.
- Change the Iceberg column type to match the actual value type (e.g. MapType or a struct).
- Add an SMT (e.g. ValueToKey/Cast) to coerce the value to string before the sink.
- 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
- Serialize structured payloads to JSON strings at the producer if the column is STRING.
- Consider Iceberg VARIANT/struct columns instead of stuffing objects into string fields.
- Ensure byte[] payloads are base64/UTF-8 encoded before sending to string columns.
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
- Cannot convert to BigDecimal
- Cannot convert to boolean
- Cannot convert to double
- Cannot convert to float
- Cannot convert to int
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)