apache/beam · error · ValueError

Ambiguous expression type (perhaps missing quoting?)

Error message

Ambiguous expression type (perhaps missing quoting?): {expr}

What it means

After normalizing string specs to {'expression': ...}, `_as_callable` requires the field spec to be a dict. If a spec is neither a string nor a dict (e.g. an int, bool, or list from YAML), it is ambiguous—likely a bare value that YAML parsed as a non-string because it was not quoted—and the transform refuses to guess.

Solutions

  1. Quote the value in the YAML file so it parses as a string: `field: "42"`.
  2. Ensure each field's value is either a plain string expression or a dict with expression/callable/path keys.
  3. Run the pipeline with the YAML validator (beam_yaml linting) to catch these before execution.

Example fix

# before (YAML parses 42 as int)
fields:
  count: 42
# after
fields:
  count: "42"
Defensive patterns

Strategy: validation

Validate before calling

for name, spec in fields.items():
    assert isinstance(spec, (str, dict)), f'field {name}: quote the value or use a dict spec'

Type guard

def is_valid_field_spec(spec) -> bool:
    if isinstance(spec, str):
        return True
    return isinstance(spec, dict) and bool(spec)

Try / catch

try:
    run_pipeline(yaml_spec)
except ValueError as e:
    if 'Ambiguous expression type' in str(e):
        raise ConfigError('unquoted YAML scalar in field spec') from e

Prevention

When it happens

Trigger: Writing a MapToFields field config whose value is an unquoted YAML scalar that parses as int/bool/float (e.g. `field: 42` or `field: yes` or `field: null`), or passing a list/dict-shape mismatch to _as_callable.

Common situations: YAML type-coercion surprises: `count: 1` intended as the string '1', `flag: yes` intended as string 'yes', or copy-pasted JSON with numbers as expression values.

Related errors


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

Appendix: source

Thrown at sdks/python/apache_beam/yaml/yaml_mapping.py:403

    return lambda row: getattr(row, fn_spec)
  else:
    return _as_callable(
        list(input_schema.keys()), fn_spec, msg, language, input_schema)


def _as_callable(original_fields, expr, transform_name, language, input_schema):
  if isinstance(expr, str):
    expr = {'expression': expr}

  # Extract original type from upstream pcoll when doing simple mappings
  original_type = input_schema.get(expr.get('expression'), None)
  if expr in original_fields:
    language = "python"

  # TODO(yaml): support an imports parameter
  # TODO(yaml): support a requirements parameter (possibly at a higher level)
  if not isinstance(expr, dict):
    raise ValueError(
        f"Ambiguous expression type (perhaps missing quoting?): {expr}")
  explicit_type = expr.pop('output_type', None)
  _check_mapping_arguments(transform_name, **expr)

  if language == "javascript":
    func = _expand_javascript_mapping_func(original_fields, **expr)
  elif language in ("python", "generic", None):
    func = _expand_python_mapping_func(original_fields, **expr)
  else:
    raise ValueError(
        f'Unknown language for mapping transform: {language}. '
        'Supported languages are "javascript" and "python."')

  if explicit_type:
    if isinstance(explicit_type, str):
      explicit_type = {'type': explicit_type}
    beam_type = json_utils.json_type_to_beam_type(explicit_type)
    validator = _validator(beam_type)

View on GitHub (pinned to 12126d8942)