{"record":{"id":"faf7ec86f0de51d8","repo":"apache/beam","slug":"ambiguous-expression-type-perhaps-missing-quoting-expr-dict","errorCode":null,"errorMessage":"Ambiguous expression type (perhaps missing quoting?): {expr_dict}","messagePattern":"Ambiguous expression type \\(perhaps missing quoting\\?\\): (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"sdks/python/apache_beam/yaml/yaml_mapping.py","lineNumber":112,"sourceCode":"def is_literal(expr: str) -> bool:\n  # Some languages have limited integer literal ranges.\n  if re.fullmatch(r'-?\\d+?', expr) and -1 << 31 < int(expr) < 1 << 31:\n    return True\n  elif re.fullmatch(r'-?\\d+\\.\\d*', expr):\n    return True\n  elif re.fullmatch(r'\"[^\\\\\"]*\"', expr):\n    return True\n  else:\n    return False\n\n\ndef validate_generic_expression(\n    expr_dict: dict,\n    input_fields: Collection[str],\n    allow_cmp: bool,\n    error_field: str) -> None:\n  if not isinstance(expr_dict, dict):\n    raise ValueError(\n        f\"Ambiguous expression type (perhaps missing quoting?): {expr_dict}\")\n  if len(expr_dict) != 1 or 'expression' not in expr_dict:\n    raise ValueError(\n        \"Missing language specification. \"\n        \"Must specify a language when using a map with custom logic for %s\" %\n        error_field)\n  expr = str(expr_dict['expression'])\n\n  def is_atomic(expr: str):\n    return is_literal(expr) or expr in input_fields\n\n  if is_atomic(expr):\n    return\n\n  if allow_cmp:\n    maybe_cmp = re.fullmatch('(.*)([<>=!]+)(.*)', expr)\n    if maybe_cmp:\n      left, cmp, right = maybe_cmp.groups()","sourceCodeStart":94,"sourceCodeEnd":130,"githubUrl":"https://github.com/apache/beam/blob/12126d8942aaf848030c478b4c6a28c6af861c66/sdks/python/apache_beam/yaml/yaml_mapping.py#L94-L130","documentation":"Beam YAML's validate_generic_expression checks that a mapping field's custom logic is a plain string expression or a dict like {expression: ...}. This ValueError fires when expr_dict is not a dict at all — e.g. a string, list, or scalar was passed where a language-specifying map was required. The 'perhaps missing quoting?' hint means a bare string that looks like an expression may have been YAML-parsed into a non-dict type.","triggerScenarios":"Calling validate_generic_expression (via validate_generic_expressions) with a mapping entry whose value is not a dict — e.g. a YAML inline mapping parsed as a string due to unquoted special characters, or a list/tuple passed in.","commonSituations":"YAML config where the expression value contains colons or braces so the parser produces a string or nested structure instead of a single-key map; users writing `expression: row.a + row.b` instead of `{expression: ...}` with a language key; programmatic API misuse passing a raw string.","solutions":["Quote the value or structure it as a single-key map, e.g. `expression: \"col + 1\"` wrapped correctly so YAML yields a dict with an 'expression' key.","Check YAML indentation — a mis-indented entry can parse as a list or scalar instead of a mapping.","In programmatic use, verify the argument is a dict before calling: isinstance(expr_dict, dict).","Use a language-tagged form like {expression: ..., language: python} if generic expressions are too ambiguous."],"exampleFix":"# before (parses ambiguously / as string)\nmapping:\n  output: col1 + : col2\n# after\ncustom:\n  language: python\n  expression: \"col1 + col2\"","handlingStrategy":"type-guard","validationCode":"if not isinstance(cfg, dict) or list(cfg.keys()) != ['expression']:\n    raise ValueError('mapping custom logic must be a single-key {expression: ...} map')","typeGuard":"def is_expr_map(v): return isinstance(v, dict) and 'expression' in v","tryCatchPattern":null,"preventionTips":["Quote YAML values containing colons or braces","Validate the pipeline config against the transform schema before submission","Prefer the explicit {expression: ..., language: ...} form"],"tags":["yaml","beam-yaml","config-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"}