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

  1. Cast the date column to timestamp: hours(CAST(d AS TIMESTAMP)).
  2. Parse string timestamps: hours(to_timestamp(ts_str)).
  3. If hourly granularity on a date is intended, use days(d) instead — dates have no time component.
  4. 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

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


AI-assisted analysis of apache/iceberg@86d9c8fc54 (2026-09-12). Data as JSON: /api/errors/4a548365e18c29ef. Report an issue: GitHub.