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
- Count the '?' placeholders in the SQL and supply exactly that many positional parameter values.
- Check the index in the message against the parameter list size to find the mismatch.
- Use named parameters (:name) instead of positional ones to make binding explicit.
- 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
- Generate SQL and its parameter list from one source of truth
- Prefer named parameters (:name) over positional '?'
- Add a unit test asserting placeholder count equals value count
- Review SQL edits that add/remove placeholders without touching values
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
- Unable to parse query %s
- Unable to convert query %s
- Unknown window function ${simpleName}
- Please explicitly specify windowing in SQL query using HOP/T
- Unknown DateTime type ${logicalId}
AI-assisted analysis of apache/beam@12126d8942 (2026-09-13).
Data as JSON: /api/errors/d90ef32e166506b4.
Report an issue: GitHub.