{"record":{"id":"6054005bc0cd7bcd","repo":"apache/beam","slug":"spanner-required-read-operation-sql-or-table-with-columns","errorCode":null,"errorMessage":"Spanner required read operation, sql or table with columns.","messagePattern":"Spanner required read operation, sql or table with columns\\.","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"sdks/python/apache_beam/io/gcp/experimental/spannerio.py","lineNumber":776,"sourceCode":"                table=table, columns=columns, index=index, keyset=keyset)\n        ]\n      elif sql is not None:\n        self._read_operations = [\n            ReadOperation.query(\n                sql=sql, params=params, param_types=param_types)\n        ]\n\n  def expand(self, pbegin):\n    if self._read_operations is not None and isinstance(pbegin, PBegin):\n      pcoll = pbegin.pipeline | Create(self._read_operations)\n    elif not isinstance(pbegin, PBegin):\n      if self._read_operations is not None:\n        raise ValueError(\n            \"Read operation in the constructor only works with \"\n            \"the root of the pipeline.\")\n      pcoll = pbegin\n    else:\n      raise ValueError(\n          \"Spanner required read operation, sql or table \"\n          \"with columns.\")\n\n    if self._transaction is None:\n      # reading as batch read using the spanner partitioning query to create\n      # batches.\n      p = (\n          pcoll\n          | 'Generate Partitions' >> ParDo(\n              _CreateReadPartitions(spanner_configuration=self._configuration))\n          | 'Reshuffle' >> Reshuffle()\n          | 'Read From Partitions' >> ParDo(\n              _ReadFromPartitionFn(spanner_configuration=self._configuration)))\n    else:\n      # reading as naive read, in which we don't make batches and execute the\n      # queries as a single read.\n      p = (\n          pcoll","sourceCodeStart":758,"sourceCodeEnd":794,"githubUrl":"https://github.com/apache/beam/blob/12126d8942aaf848030c478b4c6a28c6af861c66/sdks/python/apache_beam/io/gcp/experimental/spannerio.py#L758-L794","documentation":"ReadFromSpanner requires at least one source of read configuration: a ReadOperation (via read_operations), or a query (sql), or a table with columns. When none is supplied (and the transform is at the pipeline root), the library raises this ValueError because it has nothing to read.","triggerScenarios":"Calling ReadFromSpanner(project=..., instance=..., database=...) with no sql, no table/columns, and no read_operations; or passing read_operations=None when not piping a PCollection of ReadOperation objects.","commonSituations":"Copy-pasting a ReadFromSpanner template and leaving the sql/table argument empty; building read config conditionally so all arguments end up None; mixing up SpannerRead (which takes PCollection of ReadOperation) with ReadFromSpanner (which needs explicit sql/table).","solutions":["Pass a query: ReadFromSpanner(..., sql='SELECT * FROM table').","Or pass table and columns: ReadFromSpanner(..., table='mytable', columns=['col1','col2']).","Or pass read_operations=[ReadOperation(...)] when applying at pipeline root.","If reading per-element operations, pipe a PCollection of ReadOperation objects into SpannerRead instead of ReadFromSpanner."],"exampleFix":"# before\np | ReadFromSpanner(project='p', instance='i', database='d')\n# after\np | ReadFromSpanner(project='p', instance='i', database='d', sql='SELECT * FROM users')","handlingStrategy":"validation","validationCode":"def validate_read_config(sql=None, table=None, columns=None, read_operations=None):\n    if not (sql or (table and columns) or read_operations):\n        raise ValueError('ReadFromSpanner needs sql, or table+columns, or read_operations')\n    return True","typeGuard":null,"tryCatchPattern":"try:\n    result = p | ReadFromSpanner(project=pr, instance=ins, database=db, sql=sql)\nexcept ValueError as e:\n    if 'required read operation' in str(e):\n        logging.error('Missing read config for Spanner: %s', e)\n    raise","preventionTips":["Always supply sql or table+columns explicitly","Avoid conditionally-built kwargs that can all be None","Check the ReadFromSpanner signature in your Beam version before use"],"tags":["apache-beam","google-cloud-spanner","missing-argument","valueerror"],"backgroundTag":"missing-required-argument","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"}