apache/iceberg · error · UnsupportedOperationException
Expected value to be date or timestamp:
Error message
Expected value to be date or timestamp:
What it means
YearsFunction binds Iceberg's years() transform, which only accepts DATE, TIMESTAMP, or TIMESTAMP_NTZ values. doBind() throws this UnsupportedOperationException (appending the offending type's catalog string) for any other input type.
Source
Thrown at spark/v4.1/spark/src/main/java/org/apache/iceberg/spark/functions/YearsFunction.java:46
import org.apache.spark.sql.types.TimestampType;
/**
* A Spark function implementation for the Iceberg year transform.
*
* <p>Example usage: {@code SELECT system.years('source_col')}.
*/
public class YearsFunction extends UnaryUnboundFunction {
@Override
protected BoundFunction doBind(DataType valueType) {
if (valueType instanceof DateType) {
return new DateToYearsFunction();
} else if (valueType instanceof TimestampType) {
return new TimestampToYearsFunction();
} else if (valueType instanceof TimestampNTZType) {
return new TimestampNtzToYearsFunction();
} else {
throw new UnsupportedOperationException(
"Expected value to be date or timestamp: " + valueType.catalogString());
}
}
@Override
public String description() {
return name()
+ "(col) - Call Iceberg's year transform\n"
+ " col :: source column (must be date or timestamp)";
}
@Override
public String name() {
return "years";
}
private abstract static class BaseToYearsFunction extends BaseScalarFunction<Integer> {
@OverrideView on GitHub (pinned to 86d9c8fc54)
Solutions
- Cast the input: years(CAST(col AS TIMESTAMP)) or years(CAST(col AS DATE))
- If the value is a string in a known format, use to_date/to_timestamp first: years(to_timestamp(col))
- If the value is an epoch number, convert with timestamp_seconds(col) before calling years()
Example fix
// before SELECT years(event_date_str) FROM t; // after SELECT years(to_date(event_date_str)) FROM t;
Defensive patterns
Strategy: validation
Validate before calling
require(Seq("date","timestamp","timestamp_ntz").contains(valueCol.dataType.simpleString), s"years() needs date/timestamp, got ${valueCol.dataType}") Type guard
def isTemporal(t: DataType): Boolean = t match { case _: DateType | _: TimestampType | _: TimestampNTZType => true; case _ => false } Try / catch
try { ... } catch { case e: UnsupportedOperationException if e.getMessage.startsWith("Expected value to be date or timestamp") => ... } Prevention
- Cast string dates with to_date/to_timestamp before years()
- Convert epoch numbers with timestamp_seconds()
- Infer schema explicitly when reading CSV/JSON so dates are typed, not strings
When it happens
Trigger: Calling years() with a string ('2023-01-01'), bigint epoch, int, or date-formatted column without casting to DATE/TIMESTAMP first.
Common situations: Passing raw string dates read from CSV/JSON sources; passing unix-epoch BIGINT columns expecting years() to interpret them; using years() on DATE values written as strings in views.
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 truncation width to be tinyint, shortint or int
- Wrong number of inputs (expected width and value)
- Expected truncation col to be tinyint, shortint, int, bigint
- Wrong number of inputs (expected value)
- Expected value to be date or timestamp: ${valueType.catalogS
AI-assisted analysis of apache/iceberg@86d9c8fc54 (2026-09-12).
Data as JSON: /api/errors/02bcf91725346304.
Report an issue: GitHub.