apache/beam · error · RuntimeError
Unexpected data type
Error message
Unexpected data type: %s
What it means
During execute_executable_stage, the runner iterates over the elements returned by the SDK worker and only knows how to handle Elements.Data messages (plain data outputs). Any other element type (e.g. timers or other control payloads) is rejected with 'Unexpected data type'. The trivial runner simply cannot process those response kinds.
Solutions
- Avoid timers/stateful transforms when using TrivialRunner; use DirectRunner instead
- Upgrade apache_beam so trivial_runner handles the new element types
- Restructure the pipeline to emit only regular data outputs under the trivial runner
- File/patch the runner to handle the specific Elements oneof variant
Example fix
// before pipeline.run(runner=TrivialRunner()) # pipeline uses timers // after pipeline.run(runner=DirectRunner()) # timers supported
Defensive patterns
Strategy: try-catch
Validate before calling
def uses_timers_or_state(pipeline):
for t in pipeline.proto.components.transforms.values():
if 'timer' in (t.spec.urn or '').lower() or 'state' in (t.spec.urn or '').lower():
return True
return False
# if True, don't use TrivialRunner Try / catch
try:
pipeline.run(runner=TrivialRunner()).wait_until_finish()
except RuntimeError as e:
if 'Unexpected data type' in str(e):
logging.warning('TrivialRunner cannot handle this element type; using DirectRunner')
pipeline.run(runner=DirectRunner())
else:
raise Prevention
- Avoid timers/stateful DoFns when testing with TrivialRunner
- Use DirectRunner for pipelines with timers or control-plane features
- Keep SDK versions aligned so element payload types are understood
- Check pipeline URNs for timer/state usage before picking a runner
When it happens
Trigger: Running a pipeline under TrivialRunner where the executed bundle emits non-data responses — most commonly timer firings (Elements.Timer) or other control messages from the Fn API process bundle.
Common situations: Pipelines using timers/stateful DoFns tested with the trivial runner; SDK worker returning control payloads the trivial runner doesn't model; version drift between SDK producing new element types and an older trivial_runner.
Related errors
- Google Cloud Dataflow runner not available, please install…
- Runner is not a PipelineRunner object or the name of a…
- Unexpected pipeline runner
- Unsupported transform
- A BigQuery table or a query must be specified
AI-assisted analysis of apache/beam@12126d8942 (2026-09-13).
Data as JSON: /api/errors/f5d097768c912cb1.
Report an issue: GitHub.
Appendix: source
Thrown at sdks/python/apache_beam/runners/trivial_runner.py:234
process_bundle_request = beam_fn_api_pb2.InstructionRequest(
instruction_id=process_bundle_id,
process_bundle=beam_fn_api_pb2.ProcessBundleRequest(
process_bundle_descriptor_id=process_bundle_descriptor.id))
result_future = execution_state.worker_handler.control_conn.push(
process_bundle_request)
# Read the results off the data channel.
# Note that if there are multiple outputs, we may get them in any order,
# possibly interleaved.
for output in execution_state.worker_handler.data_conn.input_elements(
process_bundle_id, list(output_ops_to_pcoll.keys())):
if isinstance(output, beam_fn_api_pb2.Elements.Data):
# Adds the output to the appropriate PCollection.
execution_state.set_pcollection_contents(
output_ops_to_pcoll[output.transform_id], [output.data])
else:
# E.g. timers to set.
raise RuntimeError("Unexpected data type: %s" % output)
# Ensure the operation completed successfully.
# This result contains things like metrics and continuation tokens as well.
result = result_future.get()
if result.error:
raise RuntimeError(result.error)
if result.process_bundle.residual_roots:
# We would need to re-schedule execution of this bundle with this data.
raise NotImplementedError('SDF continuation')
if result.process_bundle.requires_finalization:
# We would need to invoke the finalization callback, on a best effort
# basis, *after* the outputs are durably committed.
raise NotImplementedError('finalization')
if result.process_bundle.elements.data:
# These should be processed just like outputs from the data channel.
raise NotImplementedError('control-channel data')
if result.process_bundle.elements.timers:
# These should be processed just like outputs from the data channel.View on GitHub (pinned to 12126d8942)