apache/iceberg · error · UnsupportedOperationException
Cannot unbind unsupported term
Error message
Cannot unbind unsupported term: ${term} What it means
ExpressionUtil.unbind(BoundTerm) can only convert BoundTransform and BoundReference back to unbound terms. Any other BoundTerm implementation cannot be reversed and triggers this UnsupportedOperationException.
Solutions
- Inspect the bound term class; if it is a BoundAggregate or other unsupported type, extract the reference via term.ref()/term.term() and unbind that
- Upgrade Iceberg to a version that handles the term type
- Rewrite the logic to use Expressions.transform(ref.name(), transform) or Expressions.ref(name) manually for the known type
- Avoid unbinding terms of unsupported kinds; keep the unbound form before binding if you need it later
Example fix
// before
UnboundTerm<?> unbound = ExpressionUtil.unbind(boundAggregate);
// after
UnboundTerm<?> unbound = boundAggregate instanceof BoundReference
? Expressions.ref(((BoundReference<?>) boundAggregate).name())
: ExpressionUtil.unbind((BoundTerm<?>) boundAggregate); Defensive patterns
Strategy: type-guard
Validate before calling
if (!(term instanceof BoundTransform) && !(term instanceof BoundReference)) {
throw new IllegalArgumentException("Cannot unbind term type: " + term.getClass());
} Type guard
boolean isUnbindable(BoundTerm<?> t) {
return t instanceof BoundTransform || t instanceof BoundReference;
} Try / catch
try { return ExpressionUtil.unbind(boundTerm); }
catch (UnsupportedOperationException e) { LOGGER.warn("unbind unsupported for {}", boundTerm.getClass()); return null; } Prevention
- Retain the original unbound term instead of unbinding later
- Only unbind BoundTransform/BoundReference instances
- Pin client and server Iceberg versions to avoid term-type drift
When it happens
Trigger: Calling ExpressionUtil.unbind(BoundTerm) (directly or via unbind(Term)) with a bound term that is not a BoundTransform or BoundReference, e.g. a custom BoundTerm implementation.
Common situations: Custom aggregate or metric term implementations; newer Iceberg term types appearing in bound expressions while running an older client; passing bound aggregates through unbind during predicate rewriting.
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
- AboveMax has no comparator
- BelowMin has no comparator
- BelowMin has no value
- Cannot change the type of BelowMin
- Cannot convert bound predicates to SQL
AI-assisted analysis of apache/iceberg@86d9c8fc54 (2026-09-12).
Data as JSON: /api/errors/26b281e8f9cffe3b.
Report an issue: GitHub.
Appendix: source
Thrown at api/src/main/java/org/apache/iceberg/expressions/ExpressionUtil.java:259
+ ")";
} else if (term instanceof NamedReference) {
return ((NamedReference<?>) term).name();
} else if (term instanceof BoundReference) {
return ((BoundReference<?>) term).name();
} else {
throw new UnsupportedOperationException("Unsupported term: " + term);
}
}
public static <T> UnboundTerm<T> unbind(BoundTerm<T> term) {
if (term instanceof BoundTransform) {
BoundTransform<?, T> bound = (BoundTransform<?, T>) term;
return Expressions.transform(bound.ref().name(), bound.transform());
} else if (term instanceof BoundReference) {
return Expressions.ref(((BoundReference<T>) term).name());
}
throw new UnsupportedOperationException("Cannot unbind unsupported term: " + term);
}
@SuppressWarnings("unchecked")
public static <T> UnboundTerm<T> unbind(Term term) {
if (term instanceof UnboundTerm) {
return (UnboundTerm<T>) term;
} else if (term instanceof BoundTerm) {
return unbind((BoundTerm<T>) term);
}
throw new UnsupportedOperationException("Cannot unbind unsupported term: " + term);
}
private static class RetainPredicatesByFieldIdVisitor
extends ExpressionVisitors.ExpressionVisitor<Expression> {
private final Schema schema;
private final boolean caseSensitive;
private final Set<Integer> retainFieldIds;View on GitHub (pinned to 86d9c8fc54)