apache/beam · error · IllegalArgumentException
Both query and table cannot be specified at the same time…
Error message
Both query and table cannot be specified at the same time for SpannerIO.read().
What it means
SpannerIO.Read's validate/expand logic allows exactly one read source: a SQL query or a table. If a ReadOperation has both query and table set, the transform cannot decide which to use and throws IllegalArgumentException at pipeline construction time (in Read's public validation path).
Solutions
- Remove .withQuery(...) if reading a table, or remove .withTable(...) if reading a query
- Inspect the ReadOperation/query+table fields to see which is being set twice
- If building from config, ensure only one of query/table keys is non-null
Example fix
// before
SpannerIO.read().withQuery("SELECT * FROM t").withTable("t")
// after
SpannerIO.read().withQuery("SELECT * FROM t") Defensive patterns
Strategy: validation
Validate before calling
boolean hasQuery = readOperation.getQuery() != null;
boolean hasTable = readOperation.getTable() != null;
if (hasQuery && hasTable) { throw new IllegalArgumentException("Set only one of query or table"); } Try / catch
try { pipeline.apply(SpannerIO.read()...); } catch (IllegalArgumentException e) { LOG.error("SpannerIO config error: {}", e.getMessage()); } Prevention
- Build reads from a single source-of-truth config key (query XOR table)
- Log the builder state before expand
- Avoid layering withQuery over defaults that set withTable
When it happens
Trigger: Building SpannerIO.read() with both .withQuery(...) and .withTable(...) on the same Read/ReadOperation, then running the pipeline.
Common situations: Programmatically composing a SpannerIO.read where defaults set a table and user code also sets a query; copy-pasted builder chains; config-driven construction where both keys are populated.
Related errors
- SpannerIO.read() requires query OR table to set with…
- Average partition bytes size has not been initialized…
- Can't set both the topic and the subscription for a…
- Cannot find Spanner table.
- Cannot set both endVersion and endTimestamp.
AI-assisted analysis of apache/beam@12126d8942 (2026-09-13).
Data as JSON: /api/errors/e17c7985e5e1837d.
Report an issue: GitHub.
Appendix: source
Thrown at sdks/java/io/google-cloud-platform/src/main/java/org/apache/beam/sdk/io/gcp/spanner/SpannerIO.java:1116
if (getReadOperation().getQuery() != null) {
return SpannerQuerySourceDef.create(getSpannerConfig(), getReadOperation().getQuery());
}
return SpannerTableSourceDef.create(
getSpannerConfig(), getReadOperation().getTable(), getReadOperation().getColumns());
}
@Override
public PCollection<Struct> expand(PBegin input) {
getSpannerConfig().validate();
checkArgument(
getTimestampBound() != null,
"SpannerIO.read() runs in a read only transaction and requires timestamp to be set "
+ "with withTimestampBound or withTimestamp method");
if (getReadOperation().getQuery() != null) {
// TODO: validate query?
if (getReadOperation().getTable() != null) {
throw new IllegalArgumentException(
"Both query and table cannot be specified at the same time for SpannerIO.read().");
}
} else if (getReadOperation().getTable() != null) {
// Assume read
checkNotNull(
getReadOperation().getColumns(),
"For a read operation SpannerIO.read() requires a list of "
+ "columns to set with withColumns method");
checkArgument(
!getReadOperation().getColumns().isEmpty(),
"For a read operation SpannerIO.read() requires a non-empty"
+ " list of columns to set with withColumns method");
} else {
throw new IllegalArgumentException(
"SpannerIO.read() requires query OR table to set with withTable OR withQuery method.");
}
final SpannerSourceDef sourceDef = createSourceDef();View on GitHub (pinned to 12126d8942)