apache/beam · error · java.lang.IllegalArgumentException

Cannot get limit count from RelNode tree with root

Error message

Cannot get limit count from RelNode tree with root ${relTypeName}

What it means

During SQL LIMIT planning, BeamEnumerableConverter walks the RelNode tree to extract the row-count limit. It recognizes only BeamSortRel (direct LIMIT) and AbstractBeamCalcRel (LIMIT under a calc); any other root shape makes planning fail with this IllegalArgumentException.

Solutions

  1. Rewrite the query so LIMIT appears in a shape the converter supports (a plain sort/limit or a calc directly over it)
  2. Check the Beam version; newer releases extend getLimitCount to more RelNode types, so upgrade
  3. If extending Beam, add a branch in getLimitCount for your RelNode type exposing its count

Example fix

// before
throw new IllegalArgumentException("Cannot get limit count from RelNode tree with root " + node.getRelTypeName());
// after
if (node instanceof BeamSetOperatorRelBase) {
  return ((BeamSetOperatorRelBase) node).getLimitCount(); // handle the new node type
}
throw new IllegalArgumentException("Cannot get limit count from RelNode tree with root " + node.getRelTypeName());
Defensive patterns

Strategy: try-catch

Validate before calling

// Verify the limit-bearing node type before triggering conversion
boolean limitSupported = root instanceof BeamSortRel || root instanceof AbstractBeamCalcRel;
if (!limitSupported) throw new IllegalStateException("LIMIT shape unsupported: " + root.getRelTypeName());

Type guard

boolean hasExtractableLimit(BeamRelNode node) {
  return node instanceof BeamSortRel || node instanceof AbstractBeamCalcRel;
}

Try / catch

try {
  sqlEnv.sqlQuery(query).evaluate();
} catch (IllegalArgumentException e) {
  if (e.getMessage().startsWith("Cannot get limit count from RelNode tree")) {
    // fall back to a query without the unsupported LIMIT wrapper or rewrite it
  } else throw e;
}

Prevention

When it happens

Trigger: Running a Beam SQL query with LIMIT whose top node above the sort is neither BeamSortRel nor AbstractBeamCalcRel, e.g. LIMIT combined with operators not yet handled by getLimitCount.

Common situations: Queries like SELECT ... LIMIT n wrapped by other relational operators (aggregates, sets, joins) so the converter sees an unsupported RelNode root; usually surfaced when enabling an enumerable-converter-based execution path.

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/5681dd1c26000c62. Report an issue: GitHub.

Appendix: source

Thrown at sdks/java/extensions/sql/src/main/java/org/apache/beam/sdk/extensions/sql/impl/rel/BeamEnumerableConverter.java:428

    public void processElement(@SuppressWarnings("unused") ProcessContext context) {
      rows.inc();
    }
  }

  private static boolean isLimitQuery(BeamRelNode node) {
    return (node instanceof BeamSortRel && ((BeamSortRel) node).isLimitOnly())
        || (node instanceof AbstractBeamCalcRel
            && ((AbstractBeamCalcRel) node).isInputSortRelAndLimitOnly());
  }

  private static int getLimitCount(BeamRelNode node) {
    if (node instanceof BeamSortRel) {
      return ((BeamSortRel) node).getCount();
    } else if (node instanceof AbstractBeamCalcRel) {
      return ((AbstractBeamCalcRel) node).getLimitCountOfSortRel();
    }

    throw new IllegalArgumentException(
        "Cannot get limit count from RelNode tree with root " + node.getRelTypeName());
  }

  private static boolean containsUnboundedPCollection(Pipeline p) {
    class BoundednessVisitor extends PipelineVisitor.Defaults {
      IsBounded boundedness = IsBounded.BOUNDED;

      @Override
      public void visitValue(PValue value, Node producer) {
        if (value instanceof PCollection) {
          boundedness = boundedness.and(((PCollection) value).isBounded());
        }
      }
    }

    BoundednessVisitor visitor = new BoundednessVisitor();
    p.traverseTopologically(visitor);
    return visitor.boundedness == IsBounded.UNBOUNDED;

View on GitHub (pinned to 12126d8942)