apache/iceberg · error · IllegalStateException
Unknown type for timestamp_ns: " + timeVal.getClass()
Error message
Unknown type for timestamp_ns: " + timeVal.getClass()
What it means
For nanosecond-precision timestamps (precision > 6), StructRowData.getTimestamp expects the wrapped StructLike to hold OffsetDateTime, LocalDateTime, or Long (epoch nanos). Encountering any other runtime type means the stored value cannot represent a timestamp_ns field, and IllegalStateException is thrown naming the offending class.
Solutions
- Convert the stored value (see the class name in the message) to OffsetDateTime, LocalDateTime, or epoch-nanos Long before it reaches StructRowData.
- Confirm the column precision in the read schema matches the physical data; reading nanos data with micros assumptions can feed wrong values.
- If using a custom writer, store timestamp_ns as Long epoch-nanos, LocalDateTime, or OffsetDateTime only.
- For Instant values, convert with odt = instant.atOffset(ZoneOffset.UTC) before wrapping.
Example fix
// before
TimestampData ts = structRowData.getTimestamp(pos, 9); // fails for Instant
// after
Object raw = struct.get(pos, Object.class);
OffsetDateTime normalized = (raw instanceof Instant i)
? i.atOffset(ZoneOffset.UTC)
: (OffsetDateTime) raw;
TimestampData ts = TimestampData.fromEpochMillis(
normalized.toInstant().toEpochMilli(), normalized.getNano() % 1_000_000); Defensive patterns
Strategy: validation
Validate before calling
Object raw = struct.get(pos, Object.class);
Preconditions.checkArgument(
precision <= 6 || raw == null || raw instanceof OffsetDateTime
|| raw instanceof LocalDateTime || raw instanceof Long,
"timestamp_ns field holds unexpected type: %s", raw == null ? "null" : raw.getClass()); Type guard
boolean isNanosTimestampRepresentable(Object v) {
return v instanceof OffsetDateTime || v instanceof LocalDateTime || v instanceof Long;
} Try / catch
try {
TimestampData ts = structRowData.getTimestamp(pos, 9);
} catch (IllegalStateException e) {
if (e.getMessage().startsWith("Unknown type for timestamp_ns")) {
log.error("Nanos timestamp column stores incompatible type", e);
throw new SchemaMismatchException(e);
}
throw e;
} Prevention
- Store timestamp_ns values as Long epoch-nanos, LocalDateTime, or OffsetDateTime only.
- Match column precision in the read schema to the physical data before scanning.
When it happens
Trigger: Reading a timestamptz_ns / timestamp_ns Iceberg column through StructRowData when the StructLike stores e.g. a BigDecimal, String, Instant, or micros-Long supplied to a nanos-precision getter.
Common situations: Reading tables written by engines that store nanosecond timestamps differently; custom StructLike adapters exposing Instant instead of OffsetDateTime/LocalDateTime; schema promotion from micros to nanos where old data holds a value in the wrong unit/type.
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
- Unknown type for int field. Type name: " +…
- Unknown type for long field. Type name: " +…
- Unknown type for timestamp_ns
- Could not set a field in the RowDataWrapper because rowData…
- errMsg(variant, "double")
AI-assisted analysis of apache/iceberg@86d9c8fc54 (2026-09-12).
Data as JSON: /api/errors/c2af360564416f5a.
Report an issue: GitHub.
Appendix: source
Thrown at flink/v2.2/flink/src/main/java/org/apache/iceberg/flink/data/StructRowData.java:205
@Override
public TimestampData getTimestamp(int pos, int precision) {
if (precision > 6) {
Object timeVal = struct.get(pos, Object.class);
if (timeVal instanceof OffsetDateTime) {
OffsetDateTime odt = (OffsetDateTime) timeVal;
return TimestampData.fromEpochMillis(
odt.toInstant().toEpochMilli(), odt.getNano() % 1_000_000);
} else if (timeVal instanceof LocalDateTime) {
LocalDateTime ldt = (LocalDateTime) timeVal;
return TimestampData.fromEpochMillis(
ldt.toInstant(ZoneOffset.UTC).toEpochMilli(), ldt.getNano() % 1_000_000);
} else if (timeVal instanceof Long) {
long timeLong = (Long) timeVal;
return TimestampData.fromEpochMillis(
Math.floorDiv(timeLong, 1_000_000L), (int) Math.floorMod(timeLong, 1_000_000L));
} else {
throw new IllegalStateException("Unknown type for timestamp_ns: " + timeVal.getClass());
}
}
long timeLong = getLong(pos);
return TimestampData.fromEpochMillis(
Math.floorDiv(timeLong, 1000L), (int) Math.floorMod(timeLong, 1000L) * 1000);
}
@Override
public <T> RawValueData<T> getRawValue(int pos) {
throw new UnsupportedOperationException("Not supported yet.");
}
@Override
public byte[] getBinary(int pos) {
return isNullAt(pos) ? null : getBinaryInternal(pos);
}
private byte[] getBinaryInternal(int pos) {View on GitHub (pinned to 86d9c8fc54)