apache/beam · critical · Exception

Requested execution of a stateful DoFn, but no user state…

Error message

Requested execution of a stateful DoFn, but no user state context is available. This likely means that the current runner does not support the execution of stateful DoFns.

What it means

Beam raises this when a DoFn is marked stateful (uses StateParam/TimerParam) but DoFnInvoker is created without a user_state_context, which supplies state/timer access backed by the runner. Without it, state and timer parameters cannot be fulfilled, so construction fails early with this exception.

Solutions

  1. Run on a runner that supports stateful DoFns (e.g. Dataflow/Flink/Spark with state support, DirectRunner where applicable) and pass a user_state_context to create_invoker
  2. In tests, supply a mock/fake UserStateContext when constructing the invoker
  3. If state is not actually required, refactor the DoFn to be stateless

Example fix

# before
invoker = DoFnInvoker.create_invoker(sig, output_processor=...)  # no context
# after
invoker = DoFnInvoker.create_invoker(
    sig,
    output_processor=...,
    user_state_context=my_user_state_context)
Defensive patterns

Strategy: validation

Validate before calling

def assert_state_supported(dofn_signature, context):
    if dofn_signature.is_stateful_dofn() and context is None:
        raise Exception('Stateful DoFn requires a runner/UserStateContext that supports state')

Type guard

def runner_supports_state(runner_name):
    return runner_name in ('DirectRunner', 'DataflowRunner', 'FlinkRunner', 'SparkRunner')

Try / catch

try:
    invoker = DoFnInvoker.create_invoker(sig, output_processor=proc,
                                         user_state_context=ctx)
except Exception as e:
    if 'no user state context' in str(e):
        logging.error('Runner does not support stateful DoFns; switch runner or refactor to stateless')
    raise

Prevention

When it happens

Trigger: Using DoFnInvoker.create_invoker (or a runner path) for a stateful DoFn without passing a user_state_context (e.g. running on a runner or test harness that lacks state support).

Common situations: Running stateful pipelines on runners without stateful DoFn support (some batch/direct/test harnesses or older runner versions); unit-testing stateful DoFns with plain create_invoker and no UserStateContext; missing --streaming or state flags on runners that gate the feature.

Understand the failure class

Background: "X is required", "must be set", "cannot be empty": the missing-required-config error family, from Vertex AI project/location to WeChat keys — this error's family across 18 libraries.

Related errors


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

Appendix: source

Thrown at sdks/python/apache_beam/runners/common.py:1494

    output_handler = _OutputHandler(
        windowing.windowfn,
        main_receivers,
        tagged_receivers,
        per_element_output_counter,
        getattr(fn, 'output_batch_converter', None),
        getattr(
            do_fn_signature.process_method.method_value,
            '_beam_yields_batches',
            False),
        getattr(
            do_fn_signature.process_batch_method.method_value,
            '_beam_yields_elements',
            False),
        check_user_dofn_output=check_user_dofn_output,
    )

    if do_fn_signature.is_stateful_dofn() and not user_state_context:
      raise Exception(
          'Requested execution of a stateful DoFn, but no user state context '
          'is available. This likely means that the current runner does not '
          'support the execution of stateful DoFns.')

    self.do_fn_invoker = DoFnInvoker.create_invoker(
        do_fn_signature,
        output_handler,
        self.context,
        side_inputs,
        args,
        kwargs,
        user_state_context=user_state_context,
        bundle_finalizer_param=self.bundle_finalizer_param)

  def process(self, windowed_value):
    # type: (WindowedValue) -> Iterable[SplitResultResidual]
    try:
      return self.do_fn_invoker.invoke_process(windowed_value)

View on GitHub (pinned to 12126d8942)