{"record":{"id":"e17c7985e5e1837d","repo":"apache/beam","slug":"both-query-and-table-cannot-be-specified-at-the-same-time","errorCode":null,"errorMessage":"Both query and table cannot be specified at the same time for SpannerIO.read().","messagePattern":"Both query and table cannot be specified at the same time for SpannerIO\\.read\\(\\)\\.","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"sdks/java/io/google-cloud-platform/src/main/java/org/apache/beam/sdk/io/gcp/spanner/SpannerIO.java","lineNumber":1116,"sourceCode":"      if (getReadOperation().getQuery() != null) {\n        return SpannerQuerySourceDef.create(getSpannerConfig(), getReadOperation().getQuery());\n      }\n      return SpannerTableSourceDef.create(\n          getSpannerConfig(), getReadOperation().getTable(), getReadOperation().getColumns());\n    }\n\n    @Override\n    public PCollection<Struct> expand(PBegin input) {\n      getSpannerConfig().validate();\n      checkArgument(\n          getTimestampBound() != null,\n          \"SpannerIO.read() runs in a read only transaction and requires timestamp to be set \"\n              + \"with withTimestampBound or withTimestamp method\");\n\n      if (getReadOperation().getQuery() != null) {\n        // TODO: validate query?\n        if (getReadOperation().getTable() != null) {\n          throw new IllegalArgumentException(\n              \"Both query and table cannot be specified at the same time for SpannerIO.read().\");\n        }\n      } else if (getReadOperation().getTable() != null) {\n        // Assume read\n        checkNotNull(\n            getReadOperation().getColumns(),\n            \"For a read operation SpannerIO.read() requires a list of \"\n                + \"columns to set with withColumns method\");\n        checkArgument(\n            !getReadOperation().getColumns().isEmpty(),\n            \"For a read operation SpannerIO.read() requires a non-empty\"\n                + \" list of columns to set with withColumns method\");\n      } else {\n        throw new IllegalArgumentException(\n            \"SpannerIO.read() requires query OR table to set with withTable OR withQuery method.\");\n      }\n\n      final SpannerSourceDef sourceDef = createSourceDef();","sourceCodeStart":1098,"sourceCodeEnd":1134,"githubUrl":"https://github.com/apache/beam/blob/12126d8942aaf848030c478b4c6a28c6af861c66/sdks/java/io/google-cloud-platform/src/main/java/org/apache/beam/sdk/io/gcp/spanner/SpannerIO.java#L1098-L1134","documentation":"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).","triggerScenarios":"Building SpannerIO.read() with both .withQuery(...) and .withTable(...) on the same Read/ReadOperation, then running the pipeline.","commonSituations":"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.","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"],"exampleFix":"// before\nSpannerIO.read().withQuery(\"SELECT * FROM t\").withTable(\"t\")\n// after\nSpannerIO.read().withQuery(\"SELECT * FROM t\")","handlingStrategy":"validation","validationCode":"boolean hasQuery = readOperation.getQuery() != null;\nboolean hasTable = readOperation.getTable() != null;\nif (hasQuery && hasTable) { throw new IllegalArgumentException(\"Set only one of query or table\"); }","typeGuard":null,"tryCatchPattern":"try { pipeline.apply(SpannerIO.read()...); } catch (IllegalArgumentException e) { LOG.error(\"SpannerIO config error: {}\", e.getMessage()); }","preventionTips":["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"],"tags":["java","spanner","builder","mutually-exclusive"],"backgroundTag":"mutually-exclusive-options","analyzedSha":"12126d8942aaf848030c478b4c6a28c6af861c66","analyzedAt":"2026-09-13T01:50:10.254Z","contentChangedAt":"2026-09-13T01:50:10.254Z","schemaVersion":2},"datasetVersion":"2026-09-20T03:17:13.778Z"}