apache/beam · error · TypeError

A context manager constructor (not a fully constructed conte

Error message

A context manager constructor (not a fully constructed context manager) must be passed to avoid issues with one-shot managers. For example, write {class_name}(tempfile.TemporaryDirectory, args=(...)) rather than {class_name}(tempfile.TemporaryDirectory(...))

What it means

Beam's context manager support (e.g. in DoFn/ParDo lifecycle) requires the context manager CLASS (constructor) plus args, not an already-constructed instance. Fully constructed managers may perform side effects at __init__ (like tempfile.TemporaryDirectory creating the directory immediately) or be one-shot/unpicklable when built with @contextlib.contextmanager, so Beam must construct and enter the manager itself at the right lifecycle point.

Source

Thrown at sdks/python/apache_beam/transforms/core.py:547

    self.watermark_estimator_provider = watermark_estimator_provider
    self.param_id = 'WatermarkEstimatorProvider'


class _ContextParam(_DoFnParam):
  def __init__(
      self, context_manager_constructor, args=(), kwargs=None, *, name=None):
    class_name = self.__class__.__name__.strip('_')
    if (not callable(context_manager_constructor) or
        (hasattr(context_manager_constructor, '__enter__') and
         len(inspect.signature(
             context_manager_constructor.__enter__).parameters) == 0)):
      # Context managers constructed with @contextlib.contextmanager can only
      # be used once, and in addition cannot be pickled because they invoke
      # the function on __init__ rather than at __enter__.
      # In addition, other common context managers such as
      # tempfile.TemporaryDirectory perform side-effecting actions in __init__
      # rather than in __enter__.
      raise TypeError(
          "A context manager constructor (not a fully constructed context "
          "manager) must be passed to avoid issues with one-shot managers. "
          "For example, "
          "write {class_name}(tempfile.TemporaryDirectory, args=(...)) "
          "rather than {class_name}(tempfile.TemporaryDirectory(...))")
    super().__init__(f'{class_name}_{name or id(self)}')
    self.context_manager_constructor = context_manager_constructor
    self.args = args
    self.kwargs = kwargs or {}

  def create_and_enter(self):
    cm = self.context_manager_constructor(*self.args, **self.kwargs)
    return cm, cm.__enter__()


class _BundleContextParam(_ContextParam):
  """Allows one to use a context manager to manage bundle-scoped parameters.

View on GitHub (pinned to 12126d8942)

Solutions

  1. Pass the context manager class plus constructor args, e.g. MyDoFn(tempfile.TemporaryDirectory, args=('/tmp/x',)) instead of MyDoFn(tempfile.TemporaryDirectory('/tmp/x')).
  2. For @contextlib.contextmanager-generated managers, pass the generator function and its args so Beam constructs it fresh at __enter__ time.
  3. If the manager needs setup at construction, restructure it so all side effects happen in __enter__.

Example fix

# before
do_fn = MyDoFn(tempfile.TemporaryDirectory())

# after
do_fn = MyDoFn(tempfile.TemporaryDirectory, args=())
Defensive patterns

Strategy: validation

Validate before calling

import inspect
def check_context_manager_arg(mgr):
    if inspect.isclass(mgr):
        return True
    if hasattr(mgr, '__enter__') and hasattr(mgr, '__exit__'):
        raise TypeError('Pass the context manager class + args, not an instance: '
                        f'{type(mgr).__name__} was already constructed')
    return False

Type guard

def is_cm_class(x) -> bool:
    return inspect.isclass(x) and hasattr(x, '__enter__') and hasattr(x, '__exit__')

Try / catch

try:
    do_fn = MyDoFn(cm)
except TypeError as e:
    if 'context manager constructor' in str(e):
        do_fn = MyDoFn(type(cm), args=inspect.signature(type(cm)).bind(*()).args)
    else:
        raise

Prevention

When it happens

Trigger: Passing an already-instantiated context manager to a Beam class that accepts one, e.g. DoFnWithContextManager(tempfile.TemporaryDirectory()) or similar {class_name}(manager) calls in core.py:547 __init__, instead of passing (tempfile.TemporaryDirectory, args=(...)).

Common situations: Developers porting plain Python code that uses `with tempfile.TemporaryDirectory() as d:` into a Beam DoFn; using @contextlib.contextmanager generators directly; instantiating the manager inline in a constructor call.

Related errors


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