{"record":{"id":"5788f48c5728851a","repo":"apache/beam","slug":"customqueryconfig-must-provide-a-valid-query-fn","errorCode":null,"errorMessage":"CustomQueryConfig must provide a valid query_fn","messagePattern":"CustomQueryConfig must provide a valid query_fn","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"sdks/python/apache_beam/transforms/enrichment_handlers/cloudsql.py","lineNumber":52,"sourceCode":"from sqlalchemy import create_engine\nfrom sqlalchemy import text\nfrom sqlalchemy.engine import Connection as DBAPIConnection\n\nimport apache_beam as beam\nfrom apache_beam.transforms.enrichment import EnrichmentSourceHandler\n\nQueryFn = Callable[[beam.Row], str]\nConditionValueFn = Callable[[beam.Row], list[Any]]\n\n\n@dataclass\nclass CustomQueryConfig:\n  \"\"\"Configuration for using a custom query function.\"\"\"\n  query_fn: QueryFn\n\n  def __post_init__(self):\n    if not self.query_fn:\n      raise ValueError(\"CustomQueryConfig must provide a valid query_fn\")\n\n\n@dataclass\nclass TableFieldsQueryConfig:\n  \"\"\"Configuration for using table name, where clause, and field names.\"\"\"\n  table_id: str\n  where_clause_template: str\n  where_clause_fields: list[str]\n\n  def __post_init__(self):\n    if not self.table_id or not self.where_clause_template:\n      raise ValueError(\n          \"TableFieldsQueryConfig must provide table_id and \" +\n          \"where_clause_template\")\n\n    if not self.where_clause_fields:\n      raise ValueError(\n          \"TableFieldsQueryConfig must provide non-empty \" +","sourceCodeStart":34,"sourceCodeEnd":70,"githubUrl":"https://github.com/apache/beam/blob/12126d8942aaf848030c478b4c6a28c6af861c66/sdks/python/apache_beam/transforms/enrichment_handlers/cloudsql.py#L34-L70","documentation":"cloudsql.CustomQueryConfig's __post_init__ validates that a query_fn was supplied; if it is falsy (None or empty) a ValueError is raised. CustomQueryConfig exists specifically to let users bring their own SQL query function, so without one the config is unusable.","triggerScenarios":"Constructing CustomQueryConfig() without query_fn, passing query_fn=None explicitly, or passing an empty/None value instead of a callable.","commonSituations":"Copy-pasting the dataclass and forgetting the required field; building configs dynamically from YAML/dict where the key is absent (None default).","solutions":["Pass a valid callable as query_fn, e.g. CustomQueryConfig(query_fn=my_query_fn)","If using dynamic config, check the key exists and defaults to a real function","Switch to TableFieldsQueryConfig if you intend to specify table/fields instead of a custom query"],"exampleFix":"// before\ncfg = CustomQueryConfig(query_fn=None)\n// after\ncfg = CustomQueryConfig(query_fn=lambda params, **kw: \"SELECT * FROM users WHERE id = :id\")","handlingStrategy":"validation","validationCode":"def make_custom_config(fn):\n    if not callable(fn):\n        raise ValueError('query_fn must be callable')\n    return CustomQueryConfig(query_fn=fn)","typeGuard":"def is_query_fn(v) -> bool:\n    return callable(v)","tryCatchPattern":"try:\n    cfg = CustomQueryConfig(query_fn=fn)\nexcept ValueError as e:\n    raise ValueError('supply a non-empty query_fn when using CustomQueryConfig') from e","preventionTips":["Never build dataclass configs from dicts without checking required keys","Make query_fn a keyword argument with no default so omission fails at edit time","Prefer CustomQueryConfig only when a real function exists"],"tags":["python","apache-beam","cloudsql","dataclass","validation"],"backgroundTag":"missing-required-config-field","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"}