apache/iceberg · error · java.lang.UnsupportedOperationException
Expected value to be timestamp: ${valueType.catalogString()}
Error message
Expected value to be timestamp: ${valueType.catalogString()} What it means
Iceberg's hours(x) Spark transform function only accepts timestamp or timestamp_ntz inputs — there is no hour bucketing for plain dates. Binding any other type (date, string, numeric) throws this UnsupportedOperationException with the offending type's catalogString appended. Thrown during query analysis via doBind.
Source
Thrown at spark/v4.2/spark/src/main/java/org/apache/iceberg/spark/functions/HoursFunction.java:46
import org.apache.spark.sql.types.DataTypes;
import org.apache.spark.sql.types.TimestampNTZType;
import org.apache.spark.sql.types.TimestampType;
/**
* A Spark function implementation for the Iceberg hour transform.
*
* <p>Example usage: {@code SELECT system.hours('source_col')}.
*/
public class HoursFunction extends UnaryUnboundFunction {
@Override
protected BoundFunction doBind(DataType valueType) {
if (valueType instanceof TimestampType) {
return new TimestampToHoursFunction();
} else if (valueType instanceof TimestampNTZType) {
return new TimestampNtzToHoursFunction();
} else {
throw new UnsupportedOperationException(
"Expected value to be timestamp: " + valueType.catalogString());
}
}
@Override
public String description() {
return name()
+ "(col) - Call Iceberg's hour transform\n"
+ " col :: source column (must be timestamp)";
}
@Override
public String name() {
return "hours";
}
public abstract static class BaseToHourFunction extends BaseScalarFunction<Integer>
implements ReducibleFunction<Integer, Integer> {View on GitHub (pinned to 86d9c8fc54)
Solutions
- Cast the date column to timestamp: hours(CAST(d AS TIMESTAMP)).
- Parse string timestamps: hours(to_timestamp(ts_str)).
- If hourly granularity on a date is intended, use days(d) instead — dates have no time component.
- Verify the column type with DESCRIBE TABLE and use a timestamp column.
Example fix
// before SELECT hours(d) FROM t -- d is DATE // after SELECT hours(CAST(d AS TIMESTAMP)) FROM t
Defensive patterns
Strategy: validation
Validate before calling
// Spark Scala
val dt = df.schema("value_col").dataType
require(dt.typeName.startsWith("timestamp"),
s"hours() requires TIMESTAMP or TIMESTAMP_NTZ, got: ${dt.catalogString}") Type guard
def isTimestampLike(dt: org.apache.spark.sql.types.DataType): Boolean = dt.isInstanceOf[org.apache.spark.sql.types.TimestampType] || dt.typeName == "timestamp_ntz"
Try / catch
try {
df.select(expr("hours(ts_col)"))
} catch {
case e: UnsupportedOperationException if e.getMessage.startsWith("Expected value to be timestamp") =>
throw new IllegalArgumentException("hours() needs TIMESTAMP/TIMESTAMP_NTZ; cast DATE with CAST(col AS TIMESTAMP)", e)
} Prevention
- Use days() for DATE columns; hours() only accepts timestamps
- Cast DATE to TIMESTAMP when sub-day granularity is truly needed
- Parse string timestamps with to_timestamp() before hours()
- Validate partition spec column types at table creation
When it happens
Trigger: Calling hours(value) where value is a DateType, StringType, or numeric column, e.g. hours(d) where d is DATE, or hours(ts_string).
Common situations: Applying hours() to a date column assuming it works like days(); passing string timestamps from log tables; partition spec definitions using hours on date columns.
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
- Expected value to be date or timestamp: ${valueType.catalogS
- Expected value to be date or timestamp: ${valueType.catalogS
- Expected value to be date or timestamp: ${valueType.catalogS
- Expected value to be date or timestamp: ${valueType.catalogS
- Expected value to be timestamp: ${valueType.catalogString()}
AI-assisted analysis of apache/iceberg@86d9c8fc54 (2026-09-12).
Data as JSON: /api/errors/4a548365e18c29ef.
Report an issue: GitHub.