{"record":{"id":"223a072e6f565f64","repo":"apache/beam","slug":"only-expressions-allowed-in-sql-at-name","errorCode":null,"errorMessage":"Only expressions allowed in SQL at {name}.","messagePattern":"Only expressions allowed in SQL at (.+?)\\.","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"sdks/python/apache_beam/yaml/yaml_mapping.py","lineNumber":756,"sourceCode":"\n\n@beam.ptransform.ptransform_fn\ndef _SqlFilterTransform(pcoll, sql_transform_constructor, keep, language):\n  return pcoll | sql_transform_constructor(\n      f\"SELECT * FROM PCOLLECTION WHERE {keep.get('expression')}\")\n\n\n@beam.ptransform.ptransform_fn\ndef _SqlMapToFieldsTransform(pcoll, sql_transform_constructor, **mapping_args):\n  _, fields = normalize_fields(pcoll, **mapping_args)\n\n  def extract_expr(name, v):\n    if isinstance(v, str):\n      return v\n    elif 'expression' in v:\n      return v['expression']\n    else:\n      raise ValueError(f\"Only expressions allowed in SQL at {name}.\")\n\n  selects = [\n      f'({extract_expr(name, expr)}) AS `{name}`'\n      for (name, expr) in fields.items()\n  ]\n  query = \"SELECT \" + \", \".join(selects) + \" FROM PCOLLECTION\"\n  return pcoll | sql_transform_constructor(query)\n\n\n@beam.ptransform.ptransform_fn\ndef _Partition(\n    pcoll,\n    by: Union[str, dict[str, str]],\n    outputs: list[str],\n    unknown_output: Optional[str] = None,\n    error_handling: Optional[Mapping[str, Any]] = None,\n    language: str = 'generic'):\n  \"\"\"Splits an input into several distinct outputs.","sourceCodeStart":738,"sourceCodeEnd":774,"githubUrl":"https://github.com/apache/beam/blob/12126d8942aaf848030c478b4c6a28c6af861c66/sdks/python/apache_beam/yaml/yaml_mapping.py#L738-L774","documentation":"In `extract_expr`, used by the SQL-based MapToFields path (_SqlMapToFieldsTransform), each field config must be either a plain expression string or a dict containing an 'expression' key. Any other shape (e.g. a dict with 'type'/'callable' options meant for the generic path) cannot be turned into a SQL SELECT item, so a ValueError naming the offending field is raised.","triggerScenarios":"Using language: sql (or a SQL MapToFields transform) with a `fields` entry that is a dict without an 'expression' key — e.g. {'callable': ...}, {'type': 'string'}, or {'value': ...} config copied from the generic/python transform syntax.","commonSituations":"Copy-pasting a field config from a PyJs (generic) MapToFields into a SQL one; using callable-style or type-only configs that the SQL dialect does not support; missing the nested 'expression' key.","solutions":["Wrap the expression in an 'expression' key: {field: {expression: \"...\"}}.","Pass the expression as a plain string instead of a dict.","Switch the transform to the generic language if you need callable/type-style field configs."],"exampleFix":"// before\nfields:\n  total:\n    type: double\n    value: \"price * qty\"\n// after\nfields:\n  total:\n    expression: \"(price * qty)\"\n","handlingStrategy":"type-guard","validationCode":"for name, v in fields.items():\n    if not (isinstance(v, str) or (isinstance(v, dict) and 'expression' in v)):\n        raise ValueError(f'SQL field {name} must be a string or {{expression: ...}}')","typeGuard":"def is_sql_expr(v) -> bool:\n    return isinstance(v, str) or (isinstance(v, dict) and 'expression' in v)","tryCatchPattern":"try:\n    out = sql_map_to_fields(pcoll, fields)\nexcept ValueError as e:\n    if 'Only expressions allowed in SQL' in str(e):\n        fields = {k: v['expression'] if isinstance(v, dict) and 'expression' in v else v for k, v in fields.items()}\n    else:\n        raise","preventionTips":["Use {field: {expression: ...}} or plain strings exclusively for SQL-language transforms","Do not copy callable/type-style configs from generic transforms into SQL ones","Validate the YAML spec's field shapes in CI before submitting the pipeline"],"tags":["apache-beam","yaml","sql","configuration"],"backgroundTag":"invalid-argument-format","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"}