apache/beam · error · IllegalArgumentException
Unable to unparse with operands.
Error message
Unable to unparse %s with %d operands.
What it means
BeamBigQuerySqlDialect.unparseFunctionsUsingInterval only knows how to unparse interval-function calls with exactly three operands (function name, first arg, INTERVAL qualifier). Any other operand count produces this IllegalArgumentException, naming the operator and the actual operand count.
Solutions
- Rewrite the query to use standard 3-operand interval function forms supported by the dialect
- Simplify interval expressions to INTERVAL n UNIT literals
- Extend BeamBigQuerySqlDialect.unparseFunctionsUsingInterval to handle the extra operand shapes
Example fix
// before (dialect-side)
throw new IllegalArgumentException(...);
// after: handle 2-operand form
if (operandCount == 2) { call.operand(1).unparse(writer, leftPrec, rightPrec); writer.endFunCall(frame); return; } Defensive patterns
Strategy: validation
Validate before calling
// before unparse
if (call.getOperands().size() != 3) {
throw new IllegalArgumentException("interval function " + call.getOperator().getName() + " must have 3 operands for BigQuery dialect");
} Type guard
boolean isThreeOperandInterval(RexCall call) { return call.getOperands().size() == 3; } Try / catch
try {
dialect.unparseCall(writer, call, left, right);
} catch (IllegalArgumentException e) {
if (e.getMessage().startsWith("Unable to unparse")) { /* fall back to generic unparse */ }
throw e;
} Prevention
- Keep interval function calls in the exact shape the dialect expects
- Check Calcite plan output (explain) for rewritten nodes before pushing to BigQuery
- Pin Calcite versions compatible with your Beam version
When it happens
Trigger: Unparsing a SQL call routed to unparseFunctionsUsingInterval (via unparseCall) whose operand count is not the expected 3 — e.g. a custom or Calcite-generated operator variant of an interval function with 2 or 4 operands.
Common situations: Using interval arithmetic forms the dialect doesn't model (unary interval expressions, extra precision modifiers); Calcite rewrites producing differently-shaped RexCall nodes before unparse; version mismatches between Calcite and Beam's dialect.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- Encountered an unexpected node type
- Invalid method ' '. Supported methods are: .
- Invalid write disposition
- Only INT64 is supported as the interval value for BigQuery.
- Predicate node ' ' should be a boolean expression, but was
AI-assisted analysis of apache/beam@12126d8942 (2026-09-13).
Data as JSON: /api/errors/ed651e73d3d6db19.
Report an issue: GitHub.
Appendix: source
Thrown at sdks/java/extensions/sql/src/main/java/org/apache/beam/sdk/extensions/sql/meta/provider/bigquery/BeamBigQuerySqlDialect.java:190
// TIMESTAMP_ADD(timestamp_expression, INTERVAL int64_expression date_part)
int operandCount = call.operandCount();
if (operandCount == 2) {
// operand0: timestamp_expression
// operand1: SqlIntervalLiteral (INTERVAL int64_expression date_part)
super.unparseCall(writer, call, leftPrec, rightPrec);
} else if (operandCount == 3) {
// operand0: timestamp_expression
// operand1: int64_expression
// operand2: date_part
final SqlWriter.Frame frame = writer.startFunCall(call.getOperator().getName());
call.operand(0).unparse(writer, leftPrec, rightPrec);
writer.literal(",");
writer.literal("INTERVAL");
call.operand(1).unparse(writer, leftPrec, rightPrec);
call.operand(2).unparse(writer, leftPrec, rightPrec);
writer.endFunCall(frame);
} else {
throw new IllegalArgumentException(
String.format(
"Unable to unparse %s with %d operands.",
call.getOperator().getName(), operandCount));
}
}
private void unparseExtractFunctions(
SqlWriter writer, SqlCall call, int leftPrec, int rightPrec) {
String funName = call.getOperator().getName();
int operandCount = call.operandCount();
SqlNode tz = null;
final SqlWriter.Frame frame = writer.startFunCall("EXTRACT");
if (!funName.equals("$extract") && (operandCount == 1 || operandCount == 2)) {
// EXTRACT(DATE/TIME/DATETIME FROM timestamp_expression [AT TIME ZONE tz])
// operand0: timestamp_expression
// operand1: tz (optional)
writer.literal(EXTRACT_FUNCTIONS.get(funName));View on GitHub (pinned to 12126d8942)