apache/iceberg · error · UnsupportedOperationException
Year transform is not supported
Error message
Year transform is not supported
What it means
PartitionSpecVisitor's default year() methods throw UnsupportedOperationException because a visitor that does not override year() cannot handle partition fields using the year temporal transform. The visitor dispatch (PartitionSpecVisitor.visit) detects a year transform (Dates.YEAR, Timestamps.MICROS_TO_YEAR, NANOS_TO_YEAR, or Years) and routes to year(); if the concrete visitor only implements a subset of transforms, the default implementation fails. This is an intentional fail-fast so unsupported transforms surface instead of silently producing wrong output.
Source
Thrown at api/src/main/java/org/apache/iceberg/transforms/PartitionSpecVisitor.java:57
default T bucket(String sourceName, int sourceId, int numBuckets) {
throw new UnsupportedOperationException("Bucket transform is not supported");
}
default T truncate(int fieldId, String sourceName, int sourceId, int width) {
return truncate(sourceName, sourceId, width);
}
default T truncate(String sourceName, int sourceId, int width) {
throw new UnsupportedOperationException("Truncate transform is not supported");
}
default T year(int fieldId, String sourceName, int sourceId) {
return year(sourceName, sourceId);
}
default T year(String sourceName, int sourceId) {
throw new UnsupportedOperationException("Year transform is not supported");
}
default T month(int fieldId, String sourceName, int sourceId) {
return month(sourceName, sourceId);
}
default T month(String sourceName, int sourceId) {
throw new UnsupportedOperationException("Month transform is not supported");
}
default T day(int fieldId, String sourceName, int sourceId) {
return day(sourceName, sourceId);
}
default T day(String sourceName, int sourceId) {
throw new UnsupportedOperationException("Day transform is not supported");
}
View on GitHub (pinned to 86d9c8fc54)
Solutions
- Override year(int fieldId, String sourceName, int sourceId) in your PartitionSpecVisitor implementation to handle the year transform
- If the visitor genuinely cannot support year partitioning, override year() to return null or throw a clearer domain-specific error instead of relying on the default
- Check the table's spec with spec.fields() and spec.partitionType() before running the visitor to confirm which transforms it uses
Example fix
// before
PartitionSpecVisitor<String> visitor = new PartitionSpecVisitor<String>() {
@Override
public String identity(int fieldId, String sourceName, int sourceId) { return sourceName; }
};
// after
PartitionSpecVisitor<String> visitor = new PartitionSpecVisitor<String>() {
@Override
public String identity(int fieldId, String sourceName, int sourceId) { return sourceName; }
@Override
public String year(int fieldId, String sourceName, int sourceId) {
return "years(" + sourceName + ")";
}
}; Defensive patterns
Strategy: validation
Validate before calling
boolean hasUnsupportedTransform(PartitionSpec spec) {
return spec.fields().stream().anyMatch(f -> f.transform() instanceof Years
|| f.transform() == Dates.YEAR || f.transform() == Timestamps.MICROS_TO_YEAR);
}
// call before PartitionSpecVisitor.visit and fail with your own message if true Type guard
if (field.transform() instanceof Years || field.transform() == Dates.YEAR) {
throw new IllegalStateException("Visitor does not support year transform: " + field);
} Try / catch
try {
results = PartitionSpecVisitor.visit(spec, visitor);
} catch (UnsupportedOperationException e) {
throw new IllegalArgumentException("Spec uses transforms this visitor does not support: " + e.getMessage(), e);
} Prevention
- Override every hook your source tables can use: identity, bucket, truncate, year, month, day, hour, alwaysNull, unknown
- Test visitors against specs using each Iceberg transform, including temporal ones
- Check spec.partitionType() before visiting to enumerate required transform support
When it happens
Trigger: Calling PartitionSpecVisitor.visit(spec, visitor) or visit(schema, field, visitor) on a table whose partition spec contains a year(...) partition field while the concrete visitor implementation has not overridden year(int, String, int) or year(String, int).
Common situations: Custom visitor implementations (e.g. metadata translators to other catalog formats like Hive, JDBC, or Nessie) written before year/month/day/hour overrides existed, applied to tables partitioned by years(ts). Also common after upgrading Iceberg and applying a visitor to an old table spec that uses temporal partitioning.
Understand the failure class
Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.
Related errors
- Month transform is not supported
- Day transform is not supported
- Hour transform is not supported
- Cannot bucket by type:
- Identity transform is not supported
AI-assisted analysis of apache/iceberg@86d9c8fc54 (2026-09-12).
Data as JSON: /api/errors/e24e4367298ad170.
Report an issue: GitHub.