apache/iceberg · error
Cannot replace transform in unknown predicate: + pred
Error message
Cannot replace transform in unknown predicate: + pred
What it means
ProjectionUtil.removeTransform converts a BoundPredicate whose term is a BoundTransform back into an unbound predicate over the partition column. It handles unary, literal, and set predicates; if the bound predicate is none of these, the transform cannot be stripped and this UnsupportedOperationException is thrown. In practice this is an internal invariant guard: reaching it means the predicate shape wasn't covered by the three supported categories.
Source
Thrown at api/src/main/java/org/apache/iceberg/transforms/ProjectionUtil.java:252
&& transform
.toString()
.equals(((BoundTransform<?, ?>) pred.term()).transform().toString())) {
// the bound value must be a T because the transform matches
return (UnboundPredicate<T>) removeTransform(partitionName, pred);
}
return null;
}
private static <T> UnboundPredicate<T> removeTransform(
String partitionName, BoundPredicate<T> pred) {
if (pred.isUnaryPredicate()) {
return Expressions.predicate(pred.op(), partitionName);
} else if (pred.isLiteralPredicate()) {
return Expressions.predicate(pred.op(), partitionName, pred.asLiteralPredicate().literal());
} else if (pred.isSetPredicate()) {
return Expressions.predicate(pred.op(), partitionName, pred.asSetPredicate().literalSet());
}
throw new UnsupportedOperationException(
"Cannot replace transform in unknown predicate: " + pred);
}
static <S, T> UnboundPredicate<T> transformSet(
String fieldName, BoundSetPredicate<S> predicate, Function<S, T> transform) {
return predicate(
predicate.op(),
fieldName,
Iterables.transform(predicate.asSetPredicate().literalSet(), transform::apply));
}
/**
* Fixes an inclusive projection to account for incorrectly transformed values.
*
* <p>A bug in 0.10.0 and earlier caused negative values to be incorrectly transformed by date and
* timestamp transforms to 1 larger than the correct value. For example, day(1969-12-31 10:00:00)
* produced 0 instead of -1. To read data written by versions with this bug, this method adjusts
* the inclusive projection. The current inclusive projection is correct, so this modifies theView on GitHub (pinned to 86d9c8fc54)
Solutions
- Check the predicate's op() and term() — restrict usage to standard unary (isNull/notNull), literal (==, <, >=, startsWith), and set (in/notIn) predicates
- Upgrade Iceberg — a fix may have extended removeTransform to cover the new predicate kind
- If you bind custom predicate types, project them yourself before handing the expression to partition projection (Bindings via Expressions over partition columns directly)
Example fix
// before // projection given an unsupported bound predicate kind UnsupportedOperationException: Cannot replace transform in unknown predicate: ... // after // project only standard predicate forms, e.g. rewrite to EQ/IN/LT-style bound predicates Expression projected = Projections.inclusive(expr, spec); // uses supported predicate kinds
Defensive patterns
Strategy: try-catch
Validate before calling
// Confirm the bound predicate is one of the supported shapes before projection:
boolean projectable(BoundPredicate<?> pred) {
return pred.isUnaryPredicate() || pred.isLiteralPredicate() || pred.isSetPredicate();
} Try / catch
try {
UnboundPredicate<T> p = Projections.inclusive(expr, spec); // uses projectTransformPredicate
} catch (UnsupportedOperationException e) {
if (e.getMessage().startsWith("Cannot replace transform")) {
// fall back to unprojected expression / full manifest scan
} else {
throw e;
}
} Prevention
- Only bind standard Iceberg predicates (EQ/IN/LT/GT/IS NULL/NOT) over transformed terms
- Upgrade Iceberg if you use newer predicate kinds that removeTransform may not cover
- When writing custom engines, project unsupported predicate shapes yourself before handing them to Projections
When it happens
Trigger: projectTransformPredicate matches a BoundTransform term whose transform equals the partition transform, then removeTransform encounters a BoundPredicate that is simultaneously not unary, not literal, and not set — i.e. an unrecognized/extended predicate kind reaching the projection path during expression projection to partition predicates.
Common situations: Custom expressions or third-party predicate extensions bound against a transformed term; engine integrations feeding non-standard BoundPredicate implementations into inclusive/strict projection; internal bug when a new predicate operation is added without updating removeTransform.
Understand the failure class
Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.
Related errors
- ${className} does not implement notNaN
- In expression is not supported by the visitor
- notIn expression is not supported by the visitor
- startsWith expression is not supported by the visitor
- notStartsWith expression is not supported by the visitor
AI-assisted analysis of apache/iceberg@86d9c8fc54 (2026-09-12).
Data as JSON: /api/errors/e19cf33af7f2c004.
Report an issue: GitHub.