{"record":{"id":"a1dadba349515bc5","repo":"apache/beam","slug":"invalid-input-pcolls-specified-it-must-be-a-dict","errorCode":null,"errorMessage":"Invalid input {pcolls} specified. It must be a dict.","messagePattern":"Invalid input (.+?) specified\\. It must be a dict\\.","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"sdks/python/apache_beam/yaml/yaml_join.py","lineNumber":30,"sourceCode":"# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n\"\"\"This module defines the Join operation.\"\"\"\nfrom typing import Any\nfrom typing import Optional\nfrom typing import Union\n\nimport apache_beam as beam\nfrom apache_beam.yaml import yaml_provider\n\n\ndef _validate_input(pcolls):\n  error_prefix = f'Invalid input {pcolls} specified.'\n  if not isinstance(pcolls, dict):\n    raise ValueError(f'{error_prefix} It must be a dict.')\n  if len(pcolls) < 2:\n    raise ValueError(\n        f'{error_prefix} There should be at least 2 inputs to join.')\n\n\ndef _validate_type(type, pcolls):\n  error_prefix = f'Invalid value \"{type}\" for \"type\".'\n  if not isinstance(type, dict) and not isinstance(type, str):\n    raise ValueError(f'{error_prefix} It must be a dict or a str.')\n  if isinstance(type, dict):\n    error = ValueError(\n        f'{error_prefix} When specifying a dict for type, '\n        f'it must follow this format: '\n        f'{{\"outer\": [list of inputs to outer join]}}. '\n        f'Example: {{\"outer\": [\"input1\", \"input2\"]}}')\n    if (len(type) != 1 or next(iter(type)) != 'outer' or\n        not isinstance(type['outer'], list)):\n      raise error","sourceCodeStart":12,"sourceCodeEnd":48,"githubUrl":"https://github.com/apache/beam/blob/12126d8942aaf848030c478b4c6a28c6af861c66/sdks/python/apache_beam/yaml/yaml_join.py#L12-L48","documentation":"Thrown by _validate_input in apache_beam/yaml/yaml_join.py when the join transform's inputs are not a dict mapping input tags to PCollections. SQL join requires named inputs so equality and join-type specs can reference them.","triggerScenarios":"Calling _SqlJoinTransform.expand (directly or via YAML) with inputs given as a list, tuple, or single PCollection instead of a dict like {'input1': pc1, 'input2': pc2}.","commonSituations":"Programmatic use of the YAML join transform where a developer passes a list of PCollections; YAML pipeline definitions where 'input' was serialized as an array instead of a mapping.","solutions":["Pass inputs as a dict with at least two named tags.","Check the pipeline spec: the 'input' field of Join must be an object/map, not an array.","Validate the inputs structure before expand()."],"exampleFix":"// before\nSqlJoin({'inputs': [pc1, pc2]})\n// after\nSqlJoin({'inputs': {'input1': pc1, 'input2': pc2}})","handlingStrategy":"type-guard","validationCode":"if not isinstance(inputs, dict):\n    raise TypeError('Join inputs must be a dict of tag -> PCollection')","typeGuard":"def is_valid_join_inputs(x):\n    return isinstance(x, dict) and all(isinstance(v, PCollection) for v in x.values())","tryCatchPattern":"try:\n    join.expand(inputs)\nexcept ValueError as e:\n    if 'It must be a dict' in str(e):\n        inputs = {f'input{i+1}': pc for i, pc in enumerate(inputs)}","preventionTips":["Always name inputs with explicit tags in YAML pipelines","Never pass lists of PCollections to the Join transform","Keep the 'input' section a mapping in the pipeline YAML"],"tags":["apache-beam","yaml","join","validation"],"backgroundTag":"type-mismatch","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"}