{"record":{"id":"0dd8f249292b9814","repo":"apache/beam","slug":"pcollection-used-directly-as-side-input-argument-specify","errorCode":null,"errorMessage":"PCollection used directly as side input argument. Specify AsIter(pcollection) or AsSingleton(pcollection) to indicate how the PCollection is to be used.","messagePattern":"PCollection used directly as side input argument\\. Specify AsIter\\(pcollection\\) or AsSingleton\\(pcollection\\) to indicate how the PCollection is to be used\\.","errorType":"exception","errorClass":"SideInputError","httpStatus":null,"severity":"error","filePath":"sdks/python/apache_beam/transforms/ptransform.py","lineNumber":888,"sourceCode":"\n  :class:`PTransform` s like :func:`~apache_beam.transforms.core.FlatMap`\n  invoke user-supplied code in some kind of package (e.g. a\n  :class:`~apache_beam.transforms.core.DoFn`) and optionally provide arguments\n  and side inputs to that code. This internal-use-only class contains common\n  functionality for :class:`PTransform` s that fit this model.\n  \"\"\"\n  def __init__(self, fn, *args, **kwargs):\n    # type: (WithTypeHints, *Any, **Any) -> None\n    if isinstance(fn, type) and issubclass(fn, WithTypeHints):\n      # Don't treat Fn class objects as callables.\n      raise ValueError('Use %s() not %s.' % (fn.__name__, fn.__name__))\n    self.fn = self.make_fn(fn, bool(args or kwargs))\n    # Now that we figure out the label, initialize the super-class.\n    super().__init__()\n\n    if (any(isinstance(v, pvalue.PCollection) for v in args) or\n        any(isinstance(v, pvalue.PCollection) for v in kwargs.values())):\n      raise error.SideInputError(\n          'PCollection used directly as side input argument. Specify '\n          'AsIter(pcollection) or AsSingleton(pcollection) to indicate how the '\n          'PCollection is to be used.')\n    self.args, self.kwargs, self.side_inputs = util.remove_objects_from_args(\n        args, kwargs, pvalue.AsSideInput)\n    self.raw_side_inputs = args, kwargs\n\n    # Prevent name collisions with fns of the form '<function <lambda> at ...>'\n    self._cached_fn = self.fn\n\n    # Ensure fn and side inputs are picklable for remote execution.\n    try:\n      self.fn = pickler.roundtrip(self.fn)\n    except (RuntimeError, TypeError, Exception) as e:\n      raise RuntimeError(\n          'Unable to pickle fn %s: %s. '\n          'User code must be serializable (picklable) for distributed '\n          'execution. This usually happens when lambdas or closures capture '","sourceCodeStart":870,"sourceCodeEnd":906,"githubUrl":"https://github.com/apache/beam/blob/12126d8942aaf848030c478b4c6a28c6af861c66/sdks/python/apache_beam/transforms/ptransform.py#L870-L906","documentation":"Apache Beam's PTransform.__init__ rejects a raw PCollection passed as a positional or keyword argument to a transform's fn. Side inputs must be wrapped to declare how they should be materialized (iterable or singleton). The library cannot guess the access semantics, so it fails fast at pipeline-construction time with error.SideInputError.","triggerScenarios":"Calling a DoFn/transform with a PCollection passed directly in args or kwargs, e.g. beam.Map(MyFn(), other_pcoll), instead of beam.Map(MyFn(), beam.pvalue.AsIter(other_pcoll)).","commonSituations":"Developers forget to wrap the second PCollection when joining two streams, or refactor code that previously used a plain value into one fed by another PCollection; also common when porting examples that use pvalue.AsSingleton but skipping the wrapper.","solutions":["Wrap the PCollection in the intended view: beam.pvalue.AsIter(pcoll) or beam.pvalue.AsSingleton(pcoll).","If a dict lookup is intended, use beam.pvalue.AsDict(pcoll) or AsMap(pcoll).","If the PCollection was meant to be the main input, restructure so it is the first positional input of the transform rather than an extra argument."],"exampleFix":"// before\nbeam.Map(add_offset, offsets_pcoll)\n// after\nbeam.Map(add_offset, beam.pvalue.AsSingleton(offsets_pcoll))","handlingStrategy":"validation","validationCode":"def assert_no_raw_side_inputs(fn_args, fn_kwargs):\n    for v in list(fn_args) + list(fn_kwargs.values()):\n        if isinstance(v, apache_beam.pvalue.PCollection):\n            raise ValueError(f'wrap {v} in AsIter/AsSingleton before passing as side input')","typeGuard":"def is_side_input(v) -> bool:\n    return isinstance(v, apache_beam.pvalue.AsSideInput)","tryCatchPattern":"try:\n    pcoll | beam.Map(fn, other)\nexcept apache_beam.error.SideInputError:\n    pcoll | beam.Map(fn, beam.pvalue.AsSingleton(other))","preventionTips":["Always wrap secondary PCollections in AsIter/AsSingleton/AsDict at the call site.","Add a lint/helper that asserts no PCollection appears in extra transform args.","Read the transform's DoFn signature first to decide which view type is needed."],"tags":["python","apache-beam","side-input","pipeline-construction"],"backgroundTag":"invalid-argument-value","analyzedSha":"12126d8942aaf848030c478b4c6a28c6af861c66","analyzedAt":"2026-09-13T01:50:10.254Z","contentChangedAt":"2026-09-13T01:50:10.254Z","schemaVersion":2},"datasetVersion":"2026-09-14T16:17:12.679Z"}