{"record":{"id":"85a76a538e9fbb71","repo":"apache/iceberg","slug":"cannot-convert-java-util-date-to-variant-without-a","errorCode":null,"errorMessage":"Cannot convert java.util.Date to variant without a recognized logical type schema (expected Timestamp, Time, or Date but got: ${logicalName})","messagePattern":"Cannot convert java\\.util\\.Date to variant without a recognized logical type schema \\(expected Timestamp, Time, or Date but got: (.+?)\\)","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"kafka-connect/kafka-connect/src/main/java/org/apache/iceberg/connect/data/RecordConverter.java","lineNumber":764,"sourceCode":"    if (value instanceof LocalTime localTime) {\n      return Variants.ofTime(DateTimeUtil.microsFromTime(localTime));\n    }\n    if (value instanceof Date date) {\n      String logicalName = schema != null ? schema.name() : null;\n      // Connect represents Timestamp, Time, and Date logical types as java.util.Date at runtime;\n      // normalize to Instant once, then interpret using the schema logical type name.\n      Instant connectInstant = date.toInstant();\n      if (org.apache.kafka.connect.data.Timestamp.LOGICAL_NAME.equals(logicalName)) {\n        return Variants.ofTimestamptz(DateTimeUtil.microsFromInstant(connectInstant));\n      }\n      if (org.apache.kafka.connect.data.Time.LOGICAL_NAME.equals(logicalName)) {\n        LocalTime utcTime = connectInstant.atZone(ZoneOffset.UTC).toLocalTime();\n        return Variants.ofTime(DateTimeUtil.microsFromTime(utcTime));\n      }\n      if (org.apache.kafka.connect.data.Date.LOGICAL_NAME.equals(logicalName)) {\n        return Variants.ofDate(DateTimeUtil.daysFromInstant(connectInstant));\n      }\n      throw new IllegalArgumentException(\n          \"Cannot convert java.util.Date to variant without a recognized logical type schema\"\n              + \" (expected Timestamp, Time, or Date but got: \"\n              + logicalName\n              + \")\");\n    }\n    return null;\n  }\n\n  /**\n   * Converts a Number to VariantValue; throw IllegalArgumentException if the value is not a\n   * supported number representation.\n   */\n  private static VariantValue numberToVariantValue(Number number) {\n    if (number instanceof BigDecimal bigDecimal) {\n      return Variants.of(bigDecimal);\n    }\n    if (number instanceof BigInteger bigInteger) {\n      return Variants.of(new BigDecimal(bigInteger));","sourceCodeStart":746,"sourceCodeEnd":782,"githubUrl":"https://github.com/apache/iceberg/blob/86d9c8fc543e7c56c9f624eb725f76c9baff9570/kafka-connect/kafka-connect/src/main/java/org/apache/iceberg/connect/data/RecordConverter.java#L746-L782","documentation":"temporalObjectToVariantValue throws this IllegalArgumentException when a java.util.Date is converted to a Variant but the Connect schema lacks a recognized logical type. Date is ambiguous without logical type info (Timestamp, Time, or Date); otherwise the converter cannot know whether to emit a variant timestamp, time, or date value.","triggerScenarios":"A java.util.Date value reaches variant conversion with schema null, a plain (non-logical) schema, or a logical type other than org.apache.kafka.connect.data.{Timestamp,Time,Date}.","commonSituations":"Schemaless mode (no schema attached to the record) delivering java.util.Date values; a custom logical type name from another converter; schema stripped by an SMT before the sink.","solutions":["Attach Timestamp/Time/Date logical types via Timestamp.builder()/Time.builder()/Date.builder() when building the Connect schema","Convert java.util.Date to java.time types (Instant, LocalTime, LocalDate) before the sink so no logical schema is needed","Use TimestampConverter SMT to add the proper logical type","Avoid schemaless records for variant fields containing temporal values"],"exampleFix":"// before\nSchema schema = SchemaBuilder.int64().build(); // no logical type\n// after\nSchema schema = Timestamp.builder().schema(SchemaBuilder.int64().build()).build();","handlingStrategy":"validation","validationCode":"if (value instanceof java.util.Date) {\n  String logical = schema != null && schema.name() != null ? schema.name() : null;\n  boolean ok = org.apache.kafka.connect.data.Timestamp.LOGICAL_NAME.equals(logical)\n      || org.apache.kafka.connect.data.Time.LOGICAL_NAME.equals(logical)\n      || org.apache.kafka.connect.data.Date.LOGICAL_NAME.equals(logical);\n  if (!ok) throw new IllegalStateException(\"java.util.Date needs Timestamp/Time/Date logical schema, got: \" + logical);\n}","typeGuard":"static boolean dateHasLogicalSchema(org.apache.kafka.connect.data.Schema s) {\n  if (s == null || s.name() == null) return false;\n  return Timestamp.LOGICAL_NAME.equals(s.name())\n      || Time.LOGICAL_NAME.equals(s.name())\n      || Date.LOGICAL_NAME.equals(s.name());\n}","tryCatchPattern":"try {\n  converter.convertValue(dateValue, variantType);\n} catch (IllegalArgumentException e) {\n  if (e.getMessage().contains(\"recognized logical type schema\")) {\n    log.error(\"Attach Timestamp/Time/Date logical schema or convert to java.time first\", e);\n    // fallback: convert to Instant before sinking\n  }\n}","preventionTips":["Never send java.util.Date in schemaless mode to Variant columns","Always wrap temporal Connect fields with the proper logical-type builder","Prefer java.time types (Instant, LocalDate, LocalTime) end to end","Keep SMTs from stripping schema names/logical types"],"tags":["kafka-connect","variant","logical-type","temporal"],"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"}