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 the

View on GitHub (pinned to 86d9c8fc54)

Solutions

  1. Check the predicate's op() and term() — restrict usage to standard unary (isNull/notNull), literal (==, <, >=, startsWith), and set (in/notIn) predicates
  2. Upgrade Iceberg — a fix may have extended removeTransform to cover the new predicate kind
  3. 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

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


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