{"record":{"id":"038abe7e551ca48a","repo":"apache/iceberg","slug":"cannot-convert-to-struct-value-getclass-getna","errorCode":null,"errorMessage":"Cannot convert to struct: ${value.getClass().getName()}","messagePattern":"Cannot convert to struct: (.+?)","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"kafka-connect/kafka-connect/src/main/java/org/apache/iceberg/connect/data/RecordConverter.java","lineNumber":180,"sourceCode":"      case TIMESTAMP:\n        return convertTimestampValue(value, (TimestampType) type);\n      case VARIANT:\n        return convertVariantValue(value);\n    }\n    throw new UnsupportedOperationException(\"Unsupported type: \" + type.typeId());\n  }\n\n  protected GenericRecord convertStructValue(\n      Object value,\n      StructType schema,\n      int parentFieldId,\n      SchemaUpdate.Consumer schemaUpdateConsumer) {\n    if (value instanceof Map) {\n      return convertToStruct((Map<?, ?>) value, schema, parentFieldId, schemaUpdateConsumer);\n    } else if (value instanceof Struct) {\n      return convertToStruct((Struct) value, schema, parentFieldId, schemaUpdateConsumer);\n    }\n    throw new IllegalArgumentException(\"Cannot convert to struct: \" + value.getClass().getName());\n  }\n\n  /**\n   * This method will be called for records when there is no record schema. Also, when there is no\n   * schema, we infer that map values are struct types. This method might also be called if the\n   * field value is a map but the Iceberg type is a struct. This can happen if the Iceberg table\n   * schema is not managed by the sink, i.e. created manually.\n   */\n  private GenericRecord convertToStruct(\n      Map<?, ?> map,\n      StructType schema,\n      int structFieldId,\n      SchemaUpdate.Consumer schemaUpdateConsumer) {\n    GenericRecord result = GenericRecord.create(schema);\n    map.forEach(\n        (recordFieldNameObj, recordFieldValue) -> {\n          String recordFieldName = recordFieldNameObj.toString();\n          NestedField tableField = lookupStructField(recordFieldName, schema, structFieldId);","sourceCodeStart":162,"sourceCodeEnd":198,"githubUrl":"https://github.com/apache/iceberg/blob/86d9c8fc543e7c56c9f624eb725f76c9baff9570/kafka-connect/kafka-connect/src/main/java/org/apache/iceberg/connect/data/RecordConverter.java#L162-L198","documentation":"convertStructValue requires the value to be a Connect Map or Struct because the target Iceberg type is a StructType. Any other value class triggers IllegalArgumentException with the value's class name. Unlike error 2160 this fires at nested/field level, where the target schema explicitly expects a struct.","triggerScenarios":"A record field whose Iceberg type is a struct but whose Connect value is e.g. a String, Number, List, or byte[] — passed through convert() (top level) or convertValue() (nested field).","commonSituations":"Topic schema says a field is a scalar/array but the Iceberg table schema declares it as a struct (schema drift between topic and table); JSON arrays being written to struct columns; type widened in the table after the topic schema was fixed.","solutions":["Align the Iceberg table schema with the topic schema — change the struct column to match the actual Connect type.","Fix the producer so the field is emitted as a Map/Struct matching the table's struct schema.","Provide a custom RecordConverter subclass overriding convertStructValue to coerce or reject such fields gracefully."],"exampleFix":"// before\n// table column 'address' is STRUCT, record supplies \"123 Main St\" (String)\n// after\n// change Iceberg schema to STRING, or emit:\n// Map<String, Object> address = Map.of(\"street\", \"123 Main St\");","handlingStrategy":"type-guard","validationCode":"for (Types.NestedField f : table.schema().asStruct().fields()) {\n  if (f.type().isStructType() && !(fieldValue instanceof Map) && !(fieldValue instanceof Struct)) {\n    throw new IllegalArgumentException(\"Field \" + f.name() + \" expects struct, got \" + fieldValue.getClass().getSimpleName());\n  }\n}","typeGuard":"boolean isStructLike(Object v) {\n  return v instanceof Map || v instanceof Struct;\n}","tryCatchPattern":"try {\n  Record r = converter.convert(value, consumer);\n} catch (IllegalArgumentException e) {\n  if (e.getMessage() != null && e.getMessage().contains(\"Cannot convert to struct\")) {\n    sendToDlq(record, e);\n  } else throw e;\n}","preventionTips":["Keep topic value schema and Iceberg table schema in sync; validate on deploy.","Never change an Iceberg column to struct while producers still send scalars.","Use schema registry compatibility checks to catch struct/scalar drift early."],"tags":["kafka-connect","type-mismatch","schema-drift"],"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"}