{"record":{"id":"205d58da0ef2b1f5","repo":"apache/superset","slug":"invalid-sql-error-s","errorCode":null,"errorMessage":"Invalid SQL: %(error)s","messagePattern":"Invalid SQL: (.+?)","errorType":"exception","errorClass":"SupersetVirtualTableParseException","httpStatus":400,"severity":"warning","filePath":"superset/connectors/sqla/utils.py","lineNumber":147,"sourceCode":"            raise SupersetVirtualTableParseException(\n                message=_(\"Template processing error: %(error)s\", error=str(ex)),\n            ) from ex\n        raise SupersetGenericDBErrorException(\n            message=_(\"Template processing error: %(error)s\", error=str(ex)),\n        ) from ex\n    try:\n        parsed_script = SQLScript(sql, engine=db_engine_spec.engine)\n    except SupersetParseError as ex:\n        # ``SQLScript`` fails on any invalid SQL, including static SQL\n        # with no template dependency. Only soften when the input\n        # contained Jinja markers — in that case an \"Invalid SQL\"\n        # outcome is very likely a rendering artifact (e.g. an empty\n        # ``filter_values('x')`` producing ``WHERE col IN ()``) rather\n        # than a genuine defect in the user's SQL, and the row is\n        # already persisted by ``UpdateDatasetCommand``. Genuinely\n        # invalid static SQL must still hard-error. See #38012.\n        if _has_jinja_markers(original_sql):\n            raise SupersetVirtualTableParseException(\n                message=_(\"Invalid SQL: %(error)s\", error=ex.error.message),\n            ) from ex\n        raise SupersetGenericDBErrorException(\n            message=_(\"Invalid SQL: %(error)s\", error=ex.error.message),\n        ) from ex\n    if parsed_script.has_mutation():\n        raise SupersetSecurityException(\n            SupersetError(\n                error_type=SupersetErrorType.DATASOURCE_SECURITY_ACCESS_ERROR,\n                message=_(\"Only `SELECT` statements are allowed\"),\n                level=ErrorLevel.ERROR,\n            )\n        )\n    if len(parsed_script.statements) > 1:\n        raise SupersetSecurityException(\n            SupersetError(\n                error_type=SupersetErrorType.DATASOURCE_SECURITY_ACCESS_ERROR,\n                message=_(\"Only single queries supported\"),","sourceCodeStart":129,"sourceCodeEnd":165,"githubUrl":"https://github.com/apache/superset/blob/f4587218dd19d046c3e4d00063e7d27f8a2ed354/superset/connectors/sqla/utils.py#L129-L165","documentation":"Raised by get_virtual_table_metadata() when sqlglot-based SQLScript parsing of the RENDERED SQL fails AND the original dataset SQL contained Jinja markers ({% or {{). This is the softened branch: it raises SupersetVirtualTableParseException instead of a generic DB error because, with Jinja present, parse failure is very likely a rendering artifact (e.g. an empty filter_values('x') rendering to `WHERE col IN ()`) rather than a genuine SQL defect — and the row is already persisted by UpdateDatasetCommand (see #38012).","triggerScenarios":"A virtual dataset uses `{{ filter_values('x') }}` or similar macros and, at refresh/save time, the macro renders empty or partial SQL that sqlglot cannot parse; template output produces a dangling `IN ()`, a bare `AND`, or an empty string. The check is `_has_jinja_markers(original_sql)` — any {% or {{ in the saved SQL selects this branch.","commonSituations":"Datasets built on Jinja-templated SQL that depend on dashboard context (filter_values, url_param); refreshing such datasets from the API/UI where no filter context exists, so macros render empty; saving a dataset whose SQL only parses when a Jinja variable is bound.","solutions":["Make the template total: guard macros so output is always valid SQL, e.g. `{% set vals = filter_values('x') %}{% if vals %} WHERE col IN {{ vals }}{% endif %}`.","Supply the missing template params via dataset.template_params_dict so rendering produces complete SQL.","Preview the rendered SQL (SQL Lab with the same Jinja context) to see exactly what string reaches the parser; the parse error's %(error)s names the artifact.","If the rendered SQL is genuinely invalid even with params bound, fix the SQL body itself."],"exampleFix":"-- before\nSELECT * FROM orders WHERE region IN {{ filter_values('region') }}\n\n-- after\n{% set regions = filter_values('region') %}\nSELECT * FROM orders\n{% if regions %} WHERE region IN {{ regions }}{% endif %}","handlingStrategy":"validation","validationCode":"def renders_valid_sql(dataset) -> bool:\n    try:\n        rendered = dataset.get_template_processor().process_template(\n            dataset.sql, **dataset.template_params_dict\n        )\n        SQLScript(rendered, engine=dataset.database.db_engine_spec.engine)\n        return True\n    except Exception:\n        return False","typeGuard":"from superset.exceptions import SupersetVirtualTableParseException  # softened branch marker","tryCatchPattern":"try:\n    get_virtual_table_metadata(dataset)\nexcept SupersetVirtualTableParseException as ex:\n    # rendering artifact: dataset row persists; log and let user supply context\n    logger.warning('Dataset %s needs template context: %s', dataset.id, ex)","preventionTips":["Wrap every filter_values/url_param macro in {% if %} guards so output is valid SQL with or without context.","Bind template_params on the dataset instead of relying on dashboard context at refresh time.","Treat SupersetVirtualTableParseException as 'needs context', not 'broken SQL' — don't delete the dataset."],"tags":["jinja","dataset","sql-parser","virtual-table","rendering"],"backgroundTag":null,"analyzedSha":"f4587218dd19d046c3e4d00063e7d27f8a2ed354","analyzedAt":"2026-08-14T22:39:27.425Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}