apache/iceberg · error · IllegalArgumentException
Cannot convert map to variant: keys must be non-null…
Error message
Cannot convert map to variant: keys must be non-null strings, was: ${key == null ? "null" : key.getClass().getName()} What it means
When converting a Map to an Iceberg Variant value, RecordConverter.collectFieldNames requires every key to be a non-null String. This IllegalArgumentException is thrown for maps with null or non-String keys, since Variant objects can only be keyed by strings.
Solutions
- Declare the Connect map schema with string keys: SchemaBuilder.map(SchemaBuilder.stringSchema(), valueSchema)
- Convert non-string keys to strings (String.valueOf) in an SMT before the sink
- Ensure producers never emit null keys in maps destined for variant columns
- If keys are genuinely numeric, reconsider whether Variant is the right column type
Example fix
// before SchemaBuilder.map(SchemaBuilder.int32Schema(), valueSchema) // int keys // after SchemaBuilder.map(Schema.stringSchema(), valueSchema) // string keys
Defensive patterns
Strategy: validation
Validate before calling
static void assertStringKeys(Map<?, ?> map, String path) {
map.forEach((k, v) -> {
if (!(k instanceof String)) throw new IllegalStateException(path + " has non-string key: " + k);
if (v instanceof Map<?, ?> nested) assertStringKeys(nested, path + "." + k);
});
} Type guard
static boolean hasStringKeys(Map<?, ?> map) {
return map.keySet().stream().allMatch(k -> k instanceof String);
} Try / catch
try {
converter.convertValue(mapValue, variantType);
} catch (IllegalArgumentException e) {
if (e.getMessage().contains("keys must be non-null strings")) {
log.error("Variant map has non-string keys: " + e.getMessage());
// re-key with String.valueOf before retrying
}
} Prevention
- Always declare Connect map keys as string schema
- Forbid null keys in producer maps
- Recursively validate nested maps destined for Variant columns
- Prefer structs over maps when key sets are known
When it happens
Trigger: A Kafka record's variant field is a Map with Integer/Long keys, or a null key appears in the map; collectFieldNames recurses through nested maps and hits a bad key.
Common situations: Kafka Connect map schemas often default to int keys for JSON objects or Avro maps keyed by non-strings; producers serialize JSON objects with numeric keys; a struct-shaped payload was typed as map<int,...> upstream.
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 java.util.Date to variant without a…
- Cannot convert Number to variant (unknown type)
- Cannot convert to variant
- An error occurred closing catalog instance, ignoring...
- An error occurred converting record, topic
AI-assisted analysis of apache/iceberg@86d9c8fc54 (2026-09-12).
Data as JSON: /api/errors/389cbcf367c847f0.
Report an issue: GitHub.
Appendix: source
Thrown at kafka-connect/kafka-connect/src/main/java/org/apache/iceberg/connect/data/RecordConverter.java:616
if (value instanceof Collection<?> collection) {
if (collection.isEmpty()) {
return Collections.emptySet();
}
Set<String> names = Sets.newHashSet();
collection.forEach(element -> names.addAll(collectFieldNames(element)));
return names;
} else if (value instanceof Map<?, ?> map) {
if (map.isEmpty()) {
return Collections.emptySet();
}
Set<String> names = Sets.newHashSet();
map.forEach(
(key, val) -> {
if (key instanceof String keyStr) {
names.add(keyStr);
names.addAll(collectFieldNames(val));
} else {
throw new IllegalArgumentException(
"Cannot convert map to variant: keys must be non-null strings, was: "
+ (key == null ? "null" : key.getClass().getName()));
}
});
return names;
} else if (value instanceof Struct struct) {
List<Field> fields = struct.schema().fields();
if (fields.isEmpty()) {
return Collections.emptySet();
}
Set<String> names = Sets.newHashSet();
fields.forEach(
field -> {
names.add(field.name());
names.addAll(collectFieldNames(struct.get(field)));
});
return names;
}View on GitHub (pinned to 86d9c8fc54)