{"record":{"id":"4a163539a2459e99","repo":"apache/beam","slug":"transform-identify-object-transform-is-part-of-a-chain","errorCode":null,"errorMessage":"Transform {identify_object(transform)} is part of a chain. Cannot define explicit inputs on chain pipeline","messagePattern":"Transform (.+?) is part of a chain\\. Cannot define explicit inputs on chain pipeline","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"sdks/python/apache_beam/yaml/yaml_transform.py","lineNumber":939,"sourceCode":"\n  # A chain is simply a composite transform where all inputs and outputs\n  # are implicit.\n  spec = normalize_source_sink(spec)\n  if 'transforms' not in spec:\n    raise TypeError(\n        f\"Chain at {identify_object(spec)} missing transforms property.\")\n  has_explicit_outputs = 'output' in spec\n  composite_spec = dict(normalize_inputs_outputs(tag_explicit_inputs(spec)))\n  new_transforms = []\n  for ix, transform in enumerate(composite_spec['transforms']):\n    transform = dict(transform)\n    if any(io in transform for io in ('input', 'output')):\n      if (ix == 0 and 'input' in transform and 'output' not in transform and\n          is_explicitly_empty(transform['input'])):\n        # This is OK as source clause sets an explicitly empty input.\n        pass\n      else:\n        raise ValueError(\n            f'Transform {identify_object(transform)} is part of a chain. '\n            'Cannot define explicit inputs on chain pipeline')\n    if ix == 0:\n      if is_explicitly_empty(transform.get('input', None)):\n        pass\n      elif is_explicitly_empty(composite_spec['input']):\n        transform['input'] = composite_spec['input']\n      elif is_empty(composite_spec['input']):\n        del composite_spec['input']\n      else:\n        transform['input'] = {\n            key: key\n            for key in composite_spec['input'].keys()\n        }\n    else:\n      transform['input'] = new_transforms[-1]['__uuid__']\n    new_transforms.append(transform)\n  new_transforms.extend(spec.get('extra_transforms', []))","sourceCodeStart":921,"sourceCodeEnd":957,"githubUrl":"https://github.com/apache/beam/blob/12126d8942aaf848030c478b4c6a28c6af861c66/sdks/python/apache_beam/yaml/yaml_transform.py#L921-L957","documentation":"Inside a chain transform, inputs and outputs flow implicitly from one step to the next, so individual steps may not declare their own 'input'/'output' (except the first step may set an explicitly empty input, e.g. a source). chain_as_composite raises this ValueError when any chain step defines explicit io that isn't that allowed exception.","triggerScenarios":"A transforms entry within a chain spec containing 'input:' or 'output:' keys — e.g. an intermediate step with input: previous_step, or a step with output: my_tag — anything beyond the first step's explicitly-empty input.","commonSituations":"Converting a composite pipeline to a chain and leaving explicit input/output references in the steps; adding a named output tag inside a chain to branch results; copy-pasting steps from composite sections into chain sections.","solutions":["Remove 'input'/'output' keys from chain steps; chaining wires them automatically.","If explicit wiring is needed, convert the chain to a 'composite' transform where explicit inputs/outputs are allowed.","For custom output names, use the chain's top-level 'output' override instead of per-step outputs.","If a step must start from an empty source, keep it as the first step and set its input to explicitly empty ({}), which is permitted."],"exampleFix":"// before\n- type: chain\n  transforms:\n    - type: MapToFields\n      input: source\n      config: {id: element.id}\n// after\n- type: chain\n  input: source\n  transforms:\n    - type: MapToFields\n      config: {id: element.id}","handlingStrategy":"validation","validationCode":"def check_chain_io(spec):\n    for t in spec.get('transforms', []):\n        if 'input' in t or 'output' in t:\n            raise ValueError(f\"Chain step {t.get('name', t.get('type'))} must not declare explicit input/output\")","typeGuard":"def chain_steps_are_implicit(spec) -> bool:\n    return all('input' not in t and 'output' not in t for t in spec.get('transforms', []))","tryCatchPattern":"try:\n    expand_transform(spec, scope)\nexcept ValueError as e:\n    if 'Cannot define explicit inputs on chain pipeline' in str(e):\n        print('Remove input/output keys from chain steps or convert to composite')\n    else:\n        raise","preventionTips":["Never set input/output on steps inside a chain; wiring is implicit.","Use composite transforms when per-step inputs/outputs are required.","Use the chain-level 'output' override for custom output naming."],"tags":["python","apache-beam","yaml","config"],"backgroundTag":"invalid-config-value","analyzedSha":"12126d8942aaf848030c478b4c6a28c6af861c66","analyzedAt":"2026-09-13T01:50:10.254Z","contentChangedAt":"2026-09-13T01:50:10.254Z","schemaVersion":2},"datasetVersion":"2026-09-20T03:17:13.778Z"}