{"record":{"id":"389cbcf367c847f0","repo":"apache/iceberg","slug":"cannot-convert-map-to-variant-keys-must-be-non-nu","errorCode":null,"errorMessage":"Cannot convert map to variant: keys must be non-null strings, was: ${key == null ? \"null\" : key.getClass().getName()}","messagePattern":"Cannot convert map to variant: keys must be non-null strings, was: (.+?)","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"kafka-connect/kafka-connect/src/main/java/org/apache/iceberg/connect/data/RecordConverter.java","lineNumber":616,"sourceCode":"    if (value instanceof Collection<?> collection) {\n      if (collection.isEmpty()) {\n        return Collections.emptySet();\n      }\n      Set<String> names = Sets.newHashSet();\n      collection.forEach(element -> names.addAll(collectFieldNames(element)));\n      return names;\n    } else if (value instanceof Map<?, ?> map) {\n      if (map.isEmpty()) {\n        return Collections.emptySet();\n      }\n      Set<String> names = Sets.newHashSet();\n      map.forEach(\n          (key, val) -> {\n            if (key instanceof String keyStr) {\n              names.add(keyStr);\n              names.addAll(collectFieldNames(val));\n            } else {\n              throw new IllegalArgumentException(\n                  \"Cannot convert map to variant: keys must be non-null strings, was: \"\n                      + (key == null ? \"null\" : key.getClass().getName()));\n            }\n          });\n      return names;\n    } else if (value instanceof Struct struct) {\n      List<Field> fields = struct.schema().fields();\n      if (fields.isEmpty()) {\n        return Collections.emptySet();\n      }\n      Set<String> names = Sets.newHashSet();\n      fields.forEach(\n          field -> {\n            names.add(field.name());\n            names.addAll(collectFieldNames(struct.get(field)));\n          });\n      return names;\n    }","sourceCodeStart":598,"sourceCodeEnd":634,"githubUrl":"https://github.com/apache/iceberg/blob/86d9c8fc543e7c56c9f624eb725f76c9baff9570/kafka-connect/kafka-connect/src/main/java/org/apache/iceberg/connect/data/RecordConverter.java#L598-L634","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nSchemaBuilder.map(SchemaBuilder.int32Schema(), valueSchema) // int keys\n// after\nSchemaBuilder.map(Schema.stringSchema(), valueSchema) // string keys","handlingStrategy":"validation","validationCode":"static void assertStringKeys(Map<?, ?> map, String path) {\n  map.forEach((k, v) -> {\n    if (!(k instanceof String)) throw new IllegalStateException(path + \" has non-string key: \" + k);\n    if (v instanceof Map<?, ?> nested) assertStringKeys(nested, path + \".\" + k);\n  });\n}","typeGuard":"static boolean hasStringKeys(Map<?, ?> map) {\n  return map.keySet().stream().allMatch(k -> k instanceof String);\n}","tryCatchPattern":"try {\n  converter.convertValue(mapValue, variantType);\n} catch (IllegalArgumentException e) {\n  if (e.getMessage().contains(\"keys must be non-null strings\")) {\n    log.error(\"Variant map has non-string keys: \" + e.getMessage());\n    // re-key with String.valueOf before retrying\n  }\n}","preventionTips":["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"],"tags":["kafka-connect","variant","map-keys"],"backgroundTag":"type-mismatch","analyzedSha":"86d9c8fc543e7c56c9f624eb725f76c9baff9570","analyzedAt":"2026-09-12T00:46:39.097Z","contentChangedAt":"2026-09-12T00:46:39.097Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}