apache/beam · error · java.lang.UnsupportedOperationException

Unexpected RexNode encountered

Error message

Unexpected RexNode encountered: ${className}

What it means

During I/O push-down optimization, BeamIOPushDownRule walks the RexNode expressions of a Calc to determine which input fields are used. Only RexInputRef (column refs) and RexLiteral are handled; any other RexNode subclass triggers an UnsupportedOperationException.

Solutions

  1. Simplify the SQL expression so it uses only plain column references and literals
  2. Restructure the query to move complex expressions out of the pushed-down Calc
  3. Upgrade Beam, which may add handling for more RexNode types

Example fix

// before
SELECT CASE WHEN f1 > 0 THEN f1 ELSE -f1 END FROM t
// after
SELECT f1 FROM t -- compute the CASE expression after IO push-down, outside the optimized Calc
Defensive patterns

Strategy: type-guard

Type guard

boolean isSupportedRexNode(RexNode n) { return n instanceof RexInputRef || n instanceof RexLiteral; }

Try / catch

try { plan = optimizer.transform(...) } catch (UnsupportedOperationException e) { plan = unoptimizedPlan; }

Prevention

When it happens

Trigger: A Calc node whose condition/expression list contains an unhandled RexNode type (e.g. RexCall composed in an unexpected way, RexFieldAccess) while the push-down rule calls findUtilizedInputRefs via onMatch.

Common situations: Complex SQL projections or filters (CASE expressions, nested function calls, subquery-derived refs) hitting the optimizer; often surfaces after Calcite upgrades change RexNode shape.

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/beam@12126d8942 (2026-09-13). Data as JSON: /api/errors/8f5b675eba335c63. Report an issue: GitHub.

Appendix: source

Thrown at sdks/java/extensions/sql/src/main/java/org/apache/beam/sdk/extensions/sql/impl/rule/BeamIOPushDownRule.java:210

    while (!prerequisites.isEmpty()) {
      RexNode node = prerequisites.poll();

      if (node instanceof RexCall) { // Composite expression, example: "=($t11, $t12)"
        RexCall compositeNode = (RexCall) node;

        // Expression from example above contains 2 operands: $t11, $t12
        prerequisites.addAll(compositeNode.getOperands());
      } else if (node instanceof RexInputRef) { // Input reference
        // Find a field in an inputRowType for the input reference
        int inputFieldIndex = ((RexInputRef) node).getIndex();
        RelDataTypeField field = inputRowType.getFieldList().get(inputFieldIndex);

        // If we have not seen it before - add it to the list (hash set)
        usedFields.add(field.getName());
      } else if (node instanceof RexLiteral) {
        // Does not contain information about columns utilized by a Calc
      } else {
        throw new UnsupportedOperationException(
            "Unexpected RexNode encountered: " + node.getClass().getSimpleName());
      }
    }
  }

  /**
   * Recursively reconstruct a {@code RexNode}, mapping old RexInputRefs to new.
   *
   * @param node {@code RexNode} to reconstruct.
   * @param inputRefMapping Mapping from old {@code RexInputRefNode} indexes to new, where list
   *     index is the new {@code RexInputRefNode} and the value is old {@code RexInputRefNode}.
   * @return reconstructed {@code RexNode} with {@code RexInputRefNode} remapped to new values.
   */
  @VisibleForTesting
  RexNode reMapRexNodeToNewInputs(RexNode node, List<Integer> inputRefMapping) {
    if (node instanceof RexInputRef) {
      int oldInputIndex = ((RexInputRef) node).getIndex();
      int newInputIndex = inputRefMapping.indexOf(oldInputIndex);

View on GitHub (pinned to 12126d8942)