{"record":{"id":"8c3e25f6cd21bf40","repo":"apache/beam","slug":"unsupported-beam-type-for-iceberg-timestamp-with-timezone","errorCode":null,"errorMessage":"Unsupported Beam type for Iceberg timestamp with timezone: {valueClass}","messagePattern":"Unsupported Beam type for Iceberg timestamp with timezone: (.+?)","errorType":"exception","errorClass":"UnsupportedOperationException","httpStatus":null,"severity":"error","filePath":"sdks/java/io/iceberg/src/main/java/org/apache/beam/sdk/io/iceberg/IcebergUtils.java","lineNumber":487,"sourceCode":"  private static Object getIcebergTimestampValue(Object beamValue, boolean shouldAdjustToUtc) {\n    // timestamptz\n    if (shouldAdjustToUtc) {\n      if (beamValue instanceof java.time.Instant) { // MicrosInstant\n        OffsetDateTime epoch = java.time.Instant.ofEpochSecond(0).atOffset(ZoneOffset.UTC);\n        java.time.Instant instant = (java.time.Instant) beamValue;\n        long nanosFromEpoch =\n            TimeUnit.SECONDS.toNanos(instant.getEpochSecond()) + instant.getNano();\n        return ChronoUnit.NANOS.addTo(epoch, nanosFromEpoch);\n      } else if (beamValue instanceof LocalDateTime) { // SqlTypes.DATETIME\n        return OffsetDateTime.of((LocalDateTime) beamValue, ZoneOffset.UTC);\n      } else if (beamValue instanceof Instant) { // FieldType.DATETIME\n        return DateTimeUtil.timestamptzFromMicros(((Instant) beamValue).getMillis() * 1000L);\n      } else if (beamValue instanceof Long) { // FieldType.INT64\n        return DateTimeUtil.timestamptzFromMicros((Long) beamValue);\n      } else if (beamValue instanceof String) { // FieldType.STRING\n        return OffsetDateTime.parse((String) beamValue).withOffsetSameInstant(ZoneOffset.UTC);\n      } else {\n        throw new UnsupportedOperationException(\n            \"Unsupported Beam type for Iceberg timestamp with timezone: \" + beamValue.getClass());\n      }\n    }\n\n    // timestamp\n    if (beamValue instanceof java.time.Instant) { // MicrosInstant\n      java.time.Instant instant = (java.time.Instant) beamValue;\n      return DateTimeUtil.timestampFromNanos(\n          TimeUnit.SECONDS.toNanos(instant.getEpochSecond()) + instant.getNano());\n    } else if (beamValue instanceof LocalDateTime) { // SqlType.DATETIME\n      return beamValue;\n    } else if (beamValue instanceof Instant) { // FieldType.DATETIME\n      return DateTimeUtil.timestampFromMicros(((Instant) beamValue).getMillis() * 1000L);\n    } else if (beamValue instanceof Long) { // FieldType.INT64\n      return DateTimeUtil.timestampFromMicros((Long) beamValue);\n    } else if (beamValue instanceof String) { // FieldType.STRING\n      return LocalDateTime.parse((String) beamValue);\n    } else {","sourceCodeStart":469,"sourceCodeEnd":505,"githubUrl":"https://github.com/apache/beam/blob/12126d8942aaf848030c478b4c6a28c6af861c66/sdks/java/io/iceberg/src/main/java/org/apache/beam/sdk/io/iceberg/IcebergUtils.java#L469-L505","documentation":"getIcebergTimestampValue converts a Beam value into an Iceberg timestamptz (OffsetDateTime). It accepts java.time.Instant, Long (micros since epoch), and ISO-8601 String; any other Beam runtime type is rejected with UnsupportedOperationException so a silent data corruption never reaches the Iceberg table.","triggerScenarios":"copyFieldIntoRecord maps a Beam Row field into an Iceberg record for a timestamptz column, but the Beam value is none of Instant, Long, or String — e.g. a Beam Row built with FieldType.DATETIME (org.joda.time.DateTime / ReadableInstant other than Instant), a boxed Integer, a byte array, or a custom logical type object.","commonSituations":"Reading records from another source (e.g. Avro/Parquet/JDBC) whose date-time decoder produced a different class (joda DateTime, java.util.Date, Timestamp); a Beam schema inferred a LOGICAL_TYPE whose runtime representation is not Instant; someone hand-built Rows with the wrong FieldType mapping.","solutions":["Check the Beam Row field's declared FieldType: for a timestamptz column use FieldType.DATETIME with values that are java.time.Instant (or FieldType.INT64 micros / FieldType.STRING ISO-8601)","Convert the value before writing: if you have a joda DateTime call instant.toInstant() or new java.time.Instant(...); for java.util.Date use date.toInstant()","If the value is a String, ensure it is ISO-8601 parseable by OffsetDateTime.parse, otherwise pre-normalize it","Log the offending class (beamValue.getClass()) and trace where the Row was created to fix the producer"],"exampleFix":"// before (producer built joda-based value)\nRow row = Row.withSchema(schema).addValues(DateTime.now()).build();\n// after\nRow row = Row.withSchema(schema).addValues(java.time.Instant.now()).build();","handlingStrategy":"type-guard","validationCode":"if (!(v instanceof java.time.Instant) && !(v instanceof Long) && !(v instanceof String)) {\n  throw new IllegalArgumentException(\"Field '\" + name + \"' must be Instant/Long/String for timestamptz, got \" + v.getClass());\n}","typeGuard":"boolean isTimestamptzCompatible(Object v) {\n  return v instanceof java.time.Instant || v instanceof Long || v instanceof String;\n}","tryCatchPattern":"try {\n  icebergRecord = copyFieldIntoRecord(...);\n} catch (UnsupportedOperationException e) {\n  LOG.error(\"timestamptz conversion failed: {}\", e.getMessage());\n  // coerce value to Instant or skip/retry with corrected schema\n}","preventionTips":["Use FieldType.DATETIME with java.time.Instant values in Beam Rows","Never put joda DateTime or java.util.Date into Rows destined for Iceberg timestamptz columns","Unit-test row conversion for every timestamp-producing source","Keep Strings strictly ISO-8601 with offset info"],"tags":["java","iceberg","beam","type-mismatch","datetime"],"backgroundTag":"type-mismatch","analyzedSha":"12126d8942aaf848030c478b4c6a28c6af861c66","analyzedAt":"2026-09-13T01:50:10.254Z","contentChangedAt":"2026-09-13T01:50:10.254Z","schemaVersion":2},"datasetVersion":"2026-09-14T16:17:12.679Z"}