apache/beam · error · ValueError
Unknown operation action
Error message
Unknown operation action: %s
What it means
In the Spanner write batch processing DoFn, each WriteMutation's operation kind is dispatched to the corresponding batch method (insert/update/insert_or_update/replace/delete). If a mutation carries an unrecognized operation value, the library raises this ValueError because no batch method exists for it.
Solutions
- Create mutations only via WriteMutation.insert/update/insert_or_update/replace/delete factory methods.
- Do not mutate the _operation attribute directly.
- Ensure the Beam SDK version used to serialize mutations matches the worker's SDK version (check beam_version in pipeline options).
Example fix
# before m = WriteMutation.__new__(WriteMutation); m._operation = 'upsert' # after m = WriteMutation.insert_or_update(table, rows)
Defensive patterns
Strategy: try-catch
Validate before calling
def valid_mutation(m):
return getattr(m, 'operation', None) in {
WriteMutation._OPERATION_INSERT, WriteMutation._OPERATION_UPDATE,
WriteMutation._OPERATION_INSERT_OR_UPDATE, WriteMutation._OPERATION_REPLACE,
WriteMutation._OPERATION_DELETE} Try / catch
try:
output = pcoll | spannerwrite
except ValueError as e:
if 'Unknown operation action' in str(e):
logging.error('Corrupt WriteMutation operation in stream: %s', e)
raise Prevention
- Only create mutations via WriteMutation factory methods
- Keep SDK versions consistent between job submission and runners (pickling of constants)
- Never set _operation manually
When it happens
Trigger: A WriteMutation object with a corrupted or hand-crafted _operation value (e.g. constructed by bypassing the class factory methods and setting operation directly), or a custom subclass introducing a new operation string not handled in process().
Common situations: Deserializing WriteMutation objects across Beam worker versions where operation constants differ; monkey-patching or pickling mutations between SDK versions.
Understand the failure class
Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.
Related errors
- Invalid object type: . Object must be an instance of…
- No or more than one write mutation operation provided: <
- Read operation in the constructor only works with the root…
- Spanner required read operation, sql or table with columns.
- Basepath %r must be GCS path.
AI-assisted analysis of apache/beam@12126d8942 (2026-09-13).
Data as JSON: /api/errors/b020983d861bf145.
Report an issue: GitHub.
Appendix: source
Thrown at sdks/python/apache_beam/io/gcp/experimental/spannerio.py:1261
self.batches.inc()
try:
with self._db_instance.batch() as b:
for m in element:
table_id = m.kwargs['table']
self._register_table_metric(table_id)
if m.operation == WriteMutation._OPERATION_DELETE:
batch_func = b.delete
elif m.operation == WriteMutation._OPERATION_REPLACE:
batch_func = b.replace
elif m.operation == WriteMutation._OPERATION_INSERT_OR_UPDATE:
batch_func = b.insert_or_update
elif m.operation == WriteMutation._OPERATION_INSERT:
batch_func = b.insert
elif m.operation == WriteMutation._OPERATION_UPDATE:
batch_func = b.update
else:
raise ValueError("Unknown operation action: %s" % m.operation)
batch_func(**m.kwargs)
except (ClientError, GoogleAPICallError) as e:
for service_metric in self.service_metrics.values():
service_metric.call(str(e.code.value))
raise
else:
for service_metric in self.service_metrics.values():
service_metric.call('ok')
@with_input_types(typing.Union[MutationGroup, _Mutator])
@with_output_types(MutationGroup)
class _MakeMutationGroupsFn(DoFn):
"""
Make Mutation group object if the element is the instance of _Mutator.
"""
def process(self, element):
if isinstance(element, MutationGroup):View on GitHub (pinned to 12126d8942)