apache/beam · error · IllegalArgumentException

Index out of bounds for positional parameter: ${index}

Error message

Index out of bounds for positional parameter: ${index}

What it means

The RexShuttle visitor that substitutes positional '?' parameters into the Rex tree throws IllegalArgumentException when RexDynamicParam.getIndex() falls outside the bounds of the supplied positionalParams list. The query contains more (or invalid-indexed) positional parameters than the values provided at execution time.

Source

Thrown at sdks/java/extensions/sql/src/main/java/org/apache/beam/sdk/extensions/sql/impl/CalciteQueryPlanner.java:312

      return ((BeamRelNode) rel).beamComputeSelfCost(rel.getCluster().getPlanner(), bmq);
    }
  }

  private static class ParameterBinder extends RexShuttle {
    private final RexBuilder rexBuilder;
    private final List<?> positionalParams;

    ParameterBinder(RexBuilder rexBuilder, QueryParameters params) {
      this.rexBuilder = rexBuilder;
      this.positionalParams = params.getKind() == Kind.POSITIONAL ? params.positional() : null;
    }

    @Override
    public RexNode visitDynamicParam(RexDynamicParam dynamicParam) {
      if (positionalParams != null) {
        int index = dynamicParam.getIndex();
        if (index < 0 || index >= positionalParams.size()) {
          throw new IllegalArgumentException(
              "Index out of bounds for positional parameter: " + index);
        }
        Object val = positionalParams.get(index);
        return makeLiteral(cleanValue(val), dynamicParam.getType());
      }
      return super.visitDynamicParam(dynamicParam);
    }

    private RexNode makeLiteral(Object val, RelDataType type) {
      if (val == null) {
        return rexBuilder.makeNullLiteral(type);
      }
      return rexBuilder.makeLiteral(val, type, true);
    }

    @SuppressWarnings("JavaUtilDate") // explicit java.util.Date support
    private Object cleanValue(Object value) {
      if (value instanceof org.joda.time.ReadableInstant) {

View on GitHub (pinned to 12126d8942)

Solutions

  1. Count the '?' placeholders in the SQL and supply exactly that many positional parameter values.
  2. Check the index in the message against the parameter list size to find the mismatch.
  3. Use named parameters (:name) instead of positional ones to make binding explicit.
  4. Fix the caller that builds the parameter list.

Example fix

// before
row.withValues("value1"); // query has two ?
// after
row.withValues("value1", "value2");
Defensive patterns

Strategy: validation

Validate before calling

// count placeholders and compare with supplied values
long placeholders = sql.chars().filter(c -> c == '?').count();
if (placeholders != values.size()) {
  throw new IllegalArgumentException(
      "Query has " + placeholders + " ? but " + values.size() + " values supplied");
}

Try / catch

try {
  return row.withValues(values.toArray());
} catch (IllegalArgumentException e) {
  if (e.getMessage().startsWith("Index out of bounds for positional parameter")) {
    LOG.error("Positional parameter count mismatch: {}", e.getMessage());
  }
  throw e;
}

Prevention

When it happens

Trigger: Executing a parameterized Beam SQL statement with '?' placeholders while supplying fewer Row values than the query contains (index >= positionalParams.size(), or a negative index).

Common situations: Adding a new '?' to the SQL but forgetting to append a corresponding value; passing parameters in the wrong order/count; mixing named and positional parameters.

Related errors


AI-assisted analysis of apache/beam@12126d8942 (2026-09-13). Data as JSON: /api/errors/d90ef32e166506b4. Report an issue: GitHub.