{"record":{"id":"fca461d6b35ae70a","repo":"apache/beam","slug":"f-allowed-sources-of-test-specification-identifier-must-be-a","errorCode":null,"errorMessage":"f'allowed_sources of test specification {identifier} must be a list, got {type(test_spec[\"allowed_sources\"])}'","messagePattern":"f'allowed_sources of test specification (.+?) must be a list, got (.+?)'","errorType":"validation","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"sdks/python/apache_beam/yaml/yaml_testing.py","lineNumber":149,"sourceCode":"      yaml_transform.preprocess_chain,\n      yaml_transform.tag_explicit_inputs,\n      yaml_transform.normalize_inputs_outputs,\n  ]:\n    spec = yaml_transform.apply_phase(phase, spec)\n\n  return spec\n\n\ndef validate_test_spec(test_spec):\n  if not isinstance(test_spec, dict):\n    raise TypeError(\n        f'Test specification must be an object, got {type(test_spec)}')\n  identifier = (\n      test_spec.get('name', 'unknown') +\n      f' at line {yaml_transform.SafeLineLoader.get_line(test_spec)}')\n\n  if not isinstance(test_spec.get('allowed_sources', []), list):\n    raise TypeError(\n        f'allowed_sources of test specification {identifier} '\n        f'must be a list, got {type(test_spec[\"allowed_sources\"])}')\n\n  if (not test_spec.get('expected_outputs', []) and\n      not test_spec.get('expected_inputs', [])):\n    raise ValueError(\n        f'test specification {identifier} '\n        f'must have at least one expected_outputs or expected_inputs')\n\n  unknown_attrs = set(\n      yaml_transform.SafeLineLoader.strip_metadata(test_spec).keys()) - set([\n          'name',\n          'mock_inputs',\n          'mock_outputs',\n          'expected_outputs',\n          'expected_inputs',\n          'allowed_sources',\n      ])","sourceCodeStart":131,"sourceCodeEnd":167,"githubUrl":"https://github.com/apache/beam/blob/12126d8942aaf848030c478b4c6a28c6af861c66/sdks/python/apache_beam/yaml/yaml_testing.py#L131-L167","documentation":"validate_test_spec requires the test spec's allowed_sources field, if present, to be a list of source names/types. Any other type raises a TypeError that includes the test's identifier (name and line).","triggerScenarios":"Specifying allowed_sources as a string (e.g. allowed_sources: Create), a mapping, or a comma-separated scalar instead of a YAML list.","commonSituations":"Users write a single source without list syntax; YAML stringifies it instead of producing a sequence.","solutions":["Write allowed_sources as a YAML list: allowed_sources: [Create].","Use block-list syntax (- Create) for multiple entries.","Coerce to a list in code: allowed_sources if isinstance(allowed_sources, list) else [allowed_sources]."],"exampleFix":"# before\nallowed_sources: Create\n\n# after\nallowed_sources:\n  - Create","handlingStrategy":"type-guard","validationCode":"als = test_spec.get('allowed_sources', [])\nif not isinstance(als, list):\n    test_spec['allowed_sources'] = [als]","typeGuard":"def has_list_allowed_sources(spec: dict) -> bool:\n    return isinstance(spec.get('allowed_sources', []), list)","tryCatchPattern":"try:\n    validate_test_spec(test_spec)\nexcept TypeError as e:\n    logging.error('allowed_sources must be a list: %s', e)\n    raise","preventionTips":["Always use YAML list syntax for allowed_sources","Use flow style [Create, Read] or block style consistently","Add a schema check in CI for test specs"],"tags":["python","apache-beam","yaml","testing","type-error"],"backgroundTag":"config-type-mismatch","analyzedSha":"12126d8942aaf848030c478b4c6a28c6af861c66","analyzedAt":"2026-09-13T01:50:10.254Z","contentChangedAt":"2026-09-13T01:50:10.254Z","schemaVersion":2},"datasetVersion":"2026-09-14T21:17:11.552Z"}