You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@superset.apache.org by el...@apache.org on 2026/09/12 15:11:21 UTC

(superset) 01/01: fix(sql-validate): downgrade log level for undefined Jinja function errors (SC-120409)

This is an automated email from the ASF dual-hosted git repository.

eschutho pushed a commit to branch fix-undefined-ref-validate-sql-log-level
in repository https://gitbox.apache.org/repos/asf/superset.git

commit 438258c7b0ca19c23bd2298b745110c1e7c507aa
Author: Elizabeth Thompson <es...@gmail.com>
AuthorDate: Sat Sep 12 15:11:06 2026 +0000

    fix(sql-validate): downgrade log level for undefined Jinja function errors (SC-120409)
    
    UndefinedTemplateFunctionException is a subclass of SupersetTemplateException,
    so in ValidateSQLCommand.run() it fell into the generic template-exception
    branch and was logged at ERROR with a full traceback — even though it already
    returns a clean 400 to the client. This is a user input mistake (e.g. calling a
    dbt-style ref() macro Superset does not provide), not a system fault, and is the
    sole source of Sentry noise for SUPERSET-PYTHON-ZY6 (204+ events).
    
    Add a dedicated except-clause before the SupersetTemplateException branch that
    logs at WARNING without a traceback (mirroring the sibling
    SupersetSyntaxErrorException branch and the SQL Lab execute path in
    SqlQueryRenderImpl.render, which treats this exception as expected). The
    client-facing response is unchanged — only server log severity changes.
    
    Fixes SUPERSET-PYTHON-ZY6
    
    Co-Authored-By: Claude Opus 4.8 <no...@anthropic.com>
---
 superset/commands/database/validate_sql.py         | 24 ++++++++++-
 .../commands/databases/validate_sql_test.py        | 50 ++++++++++++++++++++++
 2 files changed, 73 insertions(+), 1 deletion(-)

diff --git a/superset/commands/database/validate_sql.py b/superset/commands/database/validate_sql.py
index 17de0062b20..cabee254122 100644
--- a/superset/commands/database/validate_sql.py
+++ b/superset/commands/database/validate_sql.py
@@ -37,7 +37,10 @@ from superset.exceptions import (
     SupersetSyntaxErrorException,
     SupersetTemplateException,
 )
-from superset.jinja_context import get_template_processor
+from superset.jinja_context import (
+    get_template_processor,
+    UndefinedTemplateFunctionException,
+)
 from superset.models.core import Database
 from superset.sql_validators import get_validator_by_name
 from superset.sql_validators.base import BaseSQLValidator
@@ -110,6 +113,25 @@ class ValidateSQLCommand(BaseCommand):
                 extra={"errors": [err.message for err in ex.errors]},
             )
             raise ValidatorSQL400Error(ex.errors[0]) from ex
+        except UndefinedTemplateFunctionException as ex:
+            # The user referenced an undefined Jinja function (e.g. a dbt-style
+            # `ref(...)` macro Superset does not provide). This is a user input
+            # mistake, not a system fault, so log at WARNING without a traceback
+            # (mirrors the SupersetSyntaxErrorException branch above and the SQL
+            # Lab execute path in SqlQueryRenderImpl.render). The client-facing
+            # response is unchanged from the generic template-exception branch.
+            logger.warning(
+                "Undefined template function during SQL validation: %s", str(ex)
+            )
+            superset_error = SupersetError(
+                message=__(
+                    "Template processing failed: %(ex)s",
+                    ex=str(ex),
+                ),
+                error_type=SupersetErrorType.GENERIC_COMMAND_ERROR,
+                level=ErrorLevel.ERROR,
+            )
+            raise ValidatorSQL400Error(superset_error) from ex
         except SupersetTemplateException as ex:
             # Internal template processing errors (e.g., recursion, unexpected failures)
             logger.error(
diff --git a/tests/unit_tests/commands/databases/validate_sql_test.py b/tests/unit_tests/commands/databases/validate_sql_test.py
index 247dfc64654..79def851bc1 100644
--- a/tests/unit_tests/commands/databases/validate_sql_test.py
+++ b/tests/unit_tests/commands/databases/validate_sql_test.py
@@ -31,6 +31,7 @@ from superset.exceptions import (
     SupersetSyntaxErrorException,
     SupersetTemplateException,
 )
+from superset.jinja_context import UndefinedTemplateFunctionException
 
 
 @pytest.fixture
@@ -251,6 +252,55 @@ def test_validate_sql_template_processing_error(
     mock_validator.validate.assert_not_called()
 
 
+def test_validate_sql_undefined_template_function(
+    mock_database: MagicMock,
+    mock_validator: MagicMock,
+    mock_template_processor: MagicMock,
+    mock_config: dict[str, Any],
+    mocker: MockerFixture,
+) -> None:
+    """
+    Test that an undefined Jinja function is logged at WARNING, not ERROR.
+
+    UndefinedTemplateFunctionException is a subclass of SupersetTemplateException
+    raised when the user references an undefined Jinja function (e.g. a dbt-style
+    `ref(...)` macro). This is a user input mistake, not a system fault, so it
+    must be logged at WARNING without a traceback while still surfacing the same
+    ValidatorSQL400Error to the client. Prior to the fix it fell into the generic
+    SupersetTemplateException branch and was logged at ERROR with exc_info.
+    """
+    logger_warning = mocker.patch(
+        "superset.commands.database.validate_sql.logger.warning"
+    )
+    logger_error = mocker.patch("superset.commands.database.validate_sql.logger.error")
+
+    mock_template_processor.process_template.side_effect = (
+        UndefinedTemplateFunctionException("'ref' is undefined")
+    )
+
+    data = {
+        "sql": "SELECT * FROM {{ ref('my_model') }} LIMIT 100",
+        "schema": "public",
+        "template_params": {},
+    }
+    command = ValidateSQLCommand(model_id=1, data=data)
+
+    with pytest.raises(ValidatorSQL400Error) as exc_info:
+        command.run()
+
+    error = exc_info.value
+    assert error.error.message is not None
+    assert "Template processing failed" in error.error.message
+    assert "'ref' is undefined" in error.error.message
+    mock_validator.validate.assert_not_called()
+
+    # The whole point of the fix: log at WARNING (no traceback), not ERROR.
+    logger_warning.assert_called_once()
+    logger_error.assert_not_called()
+    # No exc_info=True passed to the warning call (no full traceback).
+    assert "exc_info" not in logger_warning.call_args.kwargs
+
+
 def test_validate_sql_generic_exception(
     mock_database: MagicMock,
     mock_validator: MagicMock,