apache/iceberg · error · ValidationException

Invalid term: , expected either a bound reference or…

Error message

Invalid term: %s, expected either a bound reference or transform

What it means

toTransform converts a bound term into its Transform: identity for a plain BoundReference, or the wrapped transform for a BoundTransform. Any other bound term shape cannot be expressed as a transform, so a ValidationException naming the term is thrown. This happens when partition terms passed to the spec update are not simple references or transformed references.

Solutions

  1. Build terms with Expressions.ref(column) or Expressions.transform(ref, transform) so they bind to BoundReference/BoundTransform
  2. Check the bound term type before passing it into the spec update
  3. Verify the term was bound against the table schema correctly

Example fix

// before
Term term = myCustomTermBuilder.build(); // not a BoundReference/BoundTransform
specUpdate.transform(term); // throws
// after
Term term = Expressions.transform(Expressions.ref("ts"), Expressions.day());
specUpdate.transform(term);
Defensive patterns

Strategy: validation

Validate before calling

if (!(term instanceof BoundReference) && !(term instanceof BoundTransform)) { throw new IllegalArgumentException("term must be a reference or transform"); }

Try / catch

catch (ValidationException e) { /* rebuild the term with Expressions helpers */ }

Prevention

When it happens

Trigger: Calling transform(Term)/addField paths in BaseUpdatePartitionSpec with a term that resolves to neither BoundReference nor BoundTransform — typically a custom, aggregate, or wrongly-constructed bound term.

Common situations: Programmatic term construction producing unexpected bound term implementations; passing projected or aggregate terms where a simple column reference or transform is required.

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


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

Appendix: source

Thrown at core/src/main/java/org/apache/iceberg/BaseUpdatePartitionSpec.java:370

    int sourceId = boundTerm.ref().fieldId();
    Transform<?, ?> transform = toTransform(boundTerm);

    Type fieldType = schema.findType(sourceId);
    if (fieldType != null) {
      transform = Transforms.fromString(fieldType, transform.toString());
    } else {
      transform = Transforms.fromString(transform.toString());
    }
    return Pair.of(sourceId, transform);
  }

  private Transform<?, ?> toTransform(BoundTerm<?> term) {
    if (term instanceof BoundReference) {
      return Transforms.identity();
    } else if (term instanceof BoundTransform) {
      return ((BoundTransform<?, ?>) term).transform();
    } else {
      throw new ValidationException(
          "Invalid term: %s, expected either a bound reference or transform", term);
    }
  }

  private void checkForRedundantAddedPartitions(PartitionField field) {
    if (isTimeTransform(field)) {
      PartitionField timeField = addedTimeFields.get(field.sourceId());
      Preconditions.checkArgument(
          timeField == null,
          "Cannot add redundant partition field: %s conflicts with %s",
          timeField,
          field);
      addedTimeFields.put(field.sourceId(), field);
    }
  }

  private static Map<String, PartitionField> indexSpecByName(PartitionSpec spec) {
    ImmutableMap.Builder<String, PartitionField> builder = ImmutableMap.builder();

View on GitHub (pinned to 86d9c8fc54)