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

  1. Override year(int fieldId, String sourceName, int sourceId) in your PartitionSpecVisitor implementation to handle the year transform
  2. 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
  3. 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

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


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