apache/beam · error · ValueError

ReadOperation is improperly configure

Error message

ReadOperation is improperly configure: %s

What it means

The ReadOperation element reaching this DoFn's process() has neither is_sql nor is_table set to True, so Beam cannot pick a transaction read action (query vs table read). It raises ValueError naming the offending element. Such an element can only come from a misconstructed ReadOperation.

Solutions

  1. Create read operations only via ReadOperation.query(sql=...) or ReadOperation.table(table=..., columns=...).
  2. Log/print the element to see why is_sql and is_table are falsy and fix the construction site.
  3. If building a custom transform, set is_sql=True or is_table=True on the ReadOperation explicitly.

Example fix

// before
op = ReadOperation(kwargs={'table': 'Users'})
// after
op = ReadOperation.table(table='Users', columns=['id', 'name'])
Defensive patterns

Strategy: validation

Validate before calling

def valid_read_op(op):
    return bool(getattr(op, 'is_sql', False) or getattr(op, 'is_table', False))

Try / catch

try:
    rows = pc | ReadRows(config)
except ValueError as e:
    if 'ReadOperation is improperly configure' in str(e):
        raise RuntimeError('Build ReadOperations via query()/table() factories') from e
    raise

Prevention

When it happens

Trigger: Constructing ReadOperation without using the ReadOperation.query() or ReadOperation.table() factory methods (e.g. ReadOperation() directly, leaving is_sql/is_table as None/False), or deserializing/corrupting a ReadOperation in a cross-language pipeline.

Common situations: Building a custom read DoFn and instantiating ReadOperation directly instead of the factories; an element silently defaulting through pickling or Beam Fn wiring.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


AI-assisted analysis of apache/beam@12126d8942 (2026-09-13). Data as JSON: /api/errors/e1e6c467dc8e7148. Report an issue: GitHub.

Appendix: source

Thrown at sdks/python/apache_beam/io/gcp/experimental/spannerio.py:428

    # side input
    self._snapshot = BatchSnapshot.from_dict(self._database, transaction_info)

    # getting the transaction from the snapshot's session to run read operation.
    # with self._snapshot.session().transaction() as transaction:
    with self._get_session().transaction() as transaction:
      table_id = self._spanner_configuration.table
      query_name = self._spanner_configuration.query_name or ''

      if element.is_sql is True:
        transaction_read = transaction.execute_sql
        metric_action = self._query_metric
        metric_id = query_name
      elif element.is_table is True:
        transaction_read = transaction.read
        metric_action = self._table_metric
        metric_id = table_id
      else:
        raise ValueError(
            "ReadOperation is improperly configure: %s" % str(element))

      try:
        for row in transaction_read(**element.kwargs):
          yield row

        metric_action(metric_id, 'ok')
      except (ClientError, GoogleAPICallError) as e:
        metric_action(metric_id, e.code.value)
        raise


@with_input_types(ReadOperation)
@with_output_types(dict[typing.Any, typing.Any])
class _CreateReadPartitions(DoFn):
  """
  A DoFn to create partitions. Uses the Partitioning API (PartitionRead /
  PartitionQuery) request to start a partitioned query operation. Returns a

View on GitHub (pinned to 12126d8942)