apache/beam · error · TypeCheckError

Transform '{full_label}' expects a PCollection as input. Got

Error message

Transform '{full_label}' expects a PCollection as input. Got a PBegin/Pipeline instead.

What it means

Beam forbids applying a ParDo directly to PBegin (the placeholder input of a Pipeline). A ParDo needs a real PCollection, so _apply_internal raises TypeCheckError with the transform's full label saying a PCollection was expected but a PBegin/Pipeline was given.

Source

Thrown at sdks/python/apache_beam/pipeline.py:806

    current = AppliedPTransform(
        self._current_transform(),
        transform,
        full_label,
        inputs,
        None,
        annotations=self._current_annotations())
    self._current_transform().add_part(current)

    try:
      self.transforms_stack.append(current)

      type_options = self._options.view_as(TypeOptions)
      if type_options.pipeline_type_check:
        transform.type_check_inputs(pvalueish)
      if isinstance(pvalueish, pvalue.PBegin) and isinstance(transform, ParDo):
        full_label = self._current_transform().full_label
        raise TypeCheckError(
            f"Transform '{full_label}' expects a PCollection as input. "
            "Got a PBegin/Pipeline instead.")

      self._assert_not_applying_PDone(pvalueish, transform)

      pvalueish_result = self.runner.apply(transform, pvalueish, self._options)

      if type_options is not None and type_options.pipeline_type_check:
        transform.type_check_outputs(pvalueish_result)

      for tag, result in ptransform.get_named_nested_pvalues(pvalueish_result):
        assert isinstance(result, (pvalue.PValue, pvalue.DoOutputsTuple))

        # Make sure we set the producer only for a leaf node in the transform
        # DAG. This way we preserve the last transform of a composite transform
        # as being the real producer of the result.
        if result.producer is None:
          result.producer = current

View on GitHub (pinned to 12126d8942)

Solutions

  1. Start the pipeline with a source: pipeline | beam.Create([...]) or a Read transform, then apply the ParDo.
  2. Check the `|` chain starts from a PCollection, not the Pipeline.
  3. If you meant a side effect/impulse, use pipeline | beam.Impulse() (which yields PBegin-compatible flow via a DoFn) carefully — generally use Create/Read.

Example fix

// before
result = pipeline | beam.Map(lambda x: x * 2)
// after
result = pipeline | beam.Create([1, 2, 3]) | beam.Map(lambda x: x * 2)
Defensive patterns

Strategy: type-guard

Validate before calling

from apache_beam.pvalue import PBegin
def ensure_not_pbegin(x):
    assert not isinstance(x, PBegin), 'ParDo needs a PCollection, not PBegin/Pipeline'

Type guard

from apache_beam.pvalue import PBegin, PCollection
def is_applyable_input(x) -> bool:
    return isinstance(x, PCollection) and not isinstance(x, PBegin)

Try / catch

try:
    out = pcoll_or_pipeline | beam.Map(fn)
except TypeCheckError as e:
    if 'PBegin/Pipeline' in str(e):
        raise ValueError('chain from a PCollection; add beam.Create/Read first') from e

Prevention

When it happens

Trigger: pipeline | beam.Map(fn) or pipeline | beam.ParDo(...) applied straight to the Pipeline object instead of a PCollection; forgetting an initial Create/Read step.

Common situations: Typos where the first transform chains off `pipeline` instead of `pcoll`; refactoring that removed the source transform; examples that mistakenly start pipelines with a Map.

Understand the failure class

Background: "is not a compatible type" / "cannot merge" errors: when a value's type doesn't match what the library requires — this error's family across 65 libraries.

Related errors


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