apache/beam · error · TypeError

f'Test specification must be an object, got {type(test_spec)

Error message

f'Test specification must be an object, got {type(test_spec)}'

What it means

Raised by validate_test_spec when the object passed to a YAML pipeline test is not a mapping (e.g. it was loaded from malformed YAML into a string or list). The validator cannot inspect test attributes like mock_inputs/expected_outputs on a non-dict, so it rejects the whole test_spec before any field checks run. The input at fault is the test_spec argument itself, typically produced by yaml.safe_load of a test file.

Source

Thrown at sdks/python/apache_beam/yaml/yaml_testing.py:142

def _preprocess_for_testing(pipeline_spec):
  spec = yaml_transform.pipeline_as_composite(pipeline_spec['pipeline'])
  # These are idempotent, so it's OK to do them preemptively.
  for phase in [
      yaml_transform.ensure_transforms_have_types,
      yaml_transform.preprocess_source_sink,
      yaml_transform.preprocess_chain,
      yaml_transform.tag_explicit_inputs,
      yaml_transform.normalize_inputs_outputs,
  ]:
    spec = yaml_transform.apply_phase(phase, spec)

  return spec


def validate_test_spec(test_spec):
  if not isinstance(test_spec, dict):
    raise TypeError(
        f'Test specification must be an object, got {type(test_spec)}')
  identifier = (
      test_spec.get('name', 'unknown') +
      f' at line {yaml_transform.SafeLineLoader.get_line(test_spec)}')

  if not isinstance(test_spec.get('allowed_sources', []), list):
    raise TypeError(
        f'allowed_sources of test specification {identifier} '
        f'must be a list, got {type(test_spec["allowed_sources"])}')

  if (not test_spec.get('expected_outputs', []) and
      not test_spec.get('expected_inputs', [])):
    raise ValueError(
        f'test specification {identifier} '
        f'must have at least one expected_outputs or expected_inputs')

  unknown_attrs = set(
      yaml_transform.SafeLineLoader.strip_metadata(test_spec).keys()) - set([

View on GitHub (pinned to 12126d8942)

Solutions

  1. Make each entry under 'tests:' a mapping with test-spec keys.
  2. Check YAML indentation so the test object's keys nest under the entry.
  3. Validate with isinstance(spec, dict) before calling validate_test_spec.

Example fix

# before
tests:
  - just_a_name

# after
tests:
  - name: just_a_name
    expected_outputs:
      out: [...]
Defensive patterns

Strategy: type-guard

Validate before calling

if not isinstance(test_spec, dict):
    raise TypeError(f'test spec must be a mapping, got {type(test_spec).__name__}')

Type guard

def is_test_spec(x) -> bool:
    return isinstance(x, dict) and ('expected_outputs' in x or 'expected_inputs' in x)

Try / catch

try:
    validate_test_spec(test_spec)
except TypeError as e:
    logging.error('Malformed test spec: %s', e)
    raise

Prevention

When it happens

Trigger: Passing a YAML test entry that is a string or list instead of a mapping with keys like name, expected_outputs, allowed_sources; e.g. 'tests: ["my_test"]'.

Common situations: Malformed YAML indentation turning the test spec into a string, or quoting the whole test object as a scalar.

Understand the failure class

Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.

Related errors


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