Skip to content

fix(sql-lab): raise SupersetTemplateException instead of raw UndefinedError in SQLExecutor.execute_async - #42851

Open
eschutho wants to merge 1 commit into
masterfrom
fix-jinja-undefined-execute-async
Open

eschutho wants to merge 1 commit into
masterfrom
fix-jinja-undefined-execute-async

Conversation

@eschutho

@eschutho eschutho commented Aug 6, 2026

Copy link
Copy Markdown
Member

SUMMARY

SQLExecutor._render_sql_template() (superset/sql/execution/executor.py) called tp.process_template() with no try/except at all. process_template() can leak a raw jinja2.exceptions.UndefinedError for a template referencing an undefined variable that isn't called as a function. execute() happens to be safe because its whole body is wrapped in a broad except Exception, but execute_async() has no such guard — the raw exception propagated straight out of the public Database.execute_async() API contract.

PROBLEM

superset/jinja_context.py::BaseTemplateProcessor.process_template() converts most Jinja rendering failures into typed exceptions, but has a bare-raise fallback for UndefinedError when the undefined variable is accessed via attribute/subscript syntax rather than a function call. This is the same gap already fixed at other process_template() call sites in this codebase: #42366, #42401, #42714, #42757, #42802 — this PR fixes it at a new call site.

_render_sql_template() is shared by both SQLExecutor.execute() and SQLExecutor.execute_async(). execute()'s entire body (including the _prepare_sql() call that reaches _render_sql_template()) is wrapped in try: ... except Exception as ex: return self._create_error_result(...), so any exception raised there already degrades gracefully into a QueryResult(status=FAILED) — not a bug. execute_async() has no equivalent guard around its _prepare_sql() call; confirmed empirically that database.execute_async("SELECT {{ missing_var[0] }}", options=QueryOptions(template_params={"foo": "bar"})) raised a bare jinja2.exceptions.UndefinedError on master, not any SupersetException. This also breaks the method's own established contract: execute_async() already raises typed SupersetSecurityException for other prep-time failures (e.g. disallowed DML), so callers reasonably expect prep-time errors to always be Superset exceptions.

FIX

Wrapped the process_template() call inside _render_sql_template() in try/except TemplateError as ex: raise SupersetTemplateException(str(ex)) from ex. Reused the existing SupersetTemplateException (status 422) rather than inventing a new exception class — it's already the established general-purpose Superset exception for Jinja template rendering failures elsewhere in this codebase (jinja_context.py itself raises it for RecursionError, and it's caught in superset/datasets/api.py and superset/commands/database/validate_sql.py).

Additive-only: no behavior change to the sync execute() path (its broad except Exception still catches the now-typed exception and returns the same QueryResult(FAILED) shape — only the error message text improves).

TESTING INSTRUCTIONS

Added two tests to tests/unit_tests/sql/execution/test_executor.py:

  • test_execute_async_undefined_template_var_raises_superset_template_exception — asserts execute_async() with a template referencing an undefined variable raises SupersetTemplateException, not a raw jinja2.exceptions.UndefinedError.

  • test_execute_sync_undefined_template_var_returns_failed_result — guard that the sync execute() path's error-handling contract (returns QueryResult(status=FAILED)) is unchanged.

  • Confirmed the regression test fails on pre-fix code: checked out the pre-fix version of executor.py (test kept), reran — the async test fails with the raw jinja2.exceptions.UndefinedError escaping uncaught.

  • Post-fix: full tests/unit_tests/sql/execution/test_executor.py passes (82/82).

  • ruff check / ruff format --check: pass on both changed files.

ADDITIONAL INFORMATION

  • Has associated tests
  • Confirmed the regression test fails on pre-fix code and passes post-fix

Tradeoffs: none — additive-only exception-handling fix, no behavior change to any currently-working path.

Related: #42366, #42401, #42714, #42757, #42802 (same process_template() bare-raise-fallback bug class, different call sites).

@dosubot dosubot Bot added global:jinja Related to Jinja templating sqllab Namespace | Anything related to the SQL Lab labels Aug 6, 2026
@bito-code-review

bito-code-review Bot commented Aug 6, 2026 •

Copy link
Copy Markdown
Contributor

Code Review Agent Run #840521

Actionable Suggestions - 0
Review Details
  • Files reviewed - 2 · Commit Range: 4a24fb5..4a24fb5
    • superset/sql/execution/executor.py
    • tests/unit_tests/sql/execution/test_executor.py
  • Files skipped - 0
  • Tools
    • MyPy (Static Code Analysis) - ✔︎ Successful
    • Astral Ruff (Static Code Analysis) - ✔︎ Successful
    • Whispers (Secret Scanner) - ✔︎ Successful
    • Detect-secrets (Secret Scanner) - ✔︎ Successful

Bito Usage Guide

Commands

Type the following command in the pull request comment and save the comment.

  • /review - Manually triggers a full AI review.

  • /pause - Pauses automatic reviews on this pull request.

  • /resume - Resumes automatic reviews.

  • /resolve - Marks all Bito-posted review comments as resolved.

  • /abort - Cancels all in-progress reviews.

Refer to the documentation for additional commands.

Configuration

This repository uses Superset You can customize the agent settings here or contact your Bito workspace admin at evan@preset.io.

Documentation & Help

AI Code Review powered by Bito Logo

Comment on lines +764 to +767
try:
return tp.process_template(sql, **template_params)
except TemplateError as ex:
raise SupersetTemplateException(str(ex)) from ex

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: The broad TemplateError catch flattens compilation-time TemplateSyntaxError exceptions raised by direct-rendering processors such as Spark and Trino into a generic SupersetTemplateException, discarding the structured SupersetSyntaxErrorException/SupersetError details that the standard processor provides. Preserve the existing typed syntax-error mapping before applying the fallback wrapper for raw render-time errors. [api mismatch]

Severity Level: Major ⚠️
- ⚠️ Spark and Trino template syntax errors lose line metadata.
- ⚠️ API responses lose structured syntax-error classification.
- ⚠️ SQL Lab users receive less actionable template diagnostics.

Fix in Cursor Fix in VSCode Claude

(Use Cmd/Ctrl + Click for best experience)

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** superset/sql/execution/executor.py
**Line:** 764:767
**Comment:**
	*Api Mismatch: The broad `TemplateError` catch flattens compilation-time `TemplateSyntaxError` exceptions raised by direct-rendering processors such as Spark and Trino into a generic `SupersetTemplateException`, discarding the structured `SupersetSyntaxErrorException`/`SupersetError` details that the standard processor provides. Preserve the existing typed syntax-error mapping before applying the fallback wrapper for raw render-time errors.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
👍 | 👎

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the flag, but I don't think this one holds — the premise is that Spark/Trino are "direct-rendering processors" distinct from the standard one, but they aren't. PrestoTemplateProcessor, HiveTemplateProcessor, SparkTemplateProcessor, and TrinoTemplateProcessor (superset/jinja_context.py:1129–1216) all inherit BaseTemplateProcessor.process_template (:986) unchanged — none override it. That base method already maps compile-time TemplateSyntaxError to a structured SupersetSyntaxErrorException (:1034) for every engine, Spark/Trino included.

Crucially, SupersetSyntaxErrorException subclasses SupersetErrorsException, not jinja2.TemplateError — so the new except TemplateError in _render_sql_template cannot catch or flatten it. The structured syntax-error mapping is fully preserved; nothing is discarded here.

What the new clause actually catches is a raw render-time TemplateError that escapes process_template (the render block at :1057 only special-cases RecursionError/UndefinedError). Before this PR that propagated as an unhandled 500; now it's a contained SupersetTemplateException. So this path is a strict improvement, not a regression.

There is a narrower, legitimate point nearby: render-time template errors don't get the same structured line-metadata mapping that compile-time syntax errors do — they land as a bare SupersetTemplateException. But that's a pre-existing inconsistency independent of this PR (which only contains a previously-unhandled leak), so I'd rather handle it as a separate follow-up than widen this change. Tracking it for a later pass.

@bito-code-review

Copy link
Copy Markdown
Contributor

The review suggestion is correct. The current implementation in superset/sql/execution/executor.py catches TemplateError and wraps it in a generic SupersetTemplateException, which loses specific error details (like SupersetSyntaxErrorException) that might be provided by the template processor. To resolve this, you should catch specific syntax-related exceptions first and only fall back to the generic SupersetTemplateException for other rendering errors.

superset/sql/execution/executor.py

try:
            return tp.process_template(sql, **template_params)
        except SupersetSyntaxErrorException:
            raise
        except TemplateError as ex:
            raise SupersetTemplateException(str(ex)) from ex

@codecov

codecov Bot commented Aug 6, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 82.46%. Comparing base (f9d67e1) to head (5617509).
⚠️ Report is 34 commits behind head on master.

Additional details and impacted files
@@            Coverage Diff             @@
##           master   #42851      +/-   ##
==========================================
+ Coverage   82.22%   82.46%   +0.24%     
==========================================
  Files        2995     2995              
  Lines      185015   188645    +3630     
  Branches    42818    43620     +802     
==========================================
+ Hits       152130   155568    +3438     
- Misses      30126    30245     +119     
- Partials     2759     2832      +73     
Flag Coverage Δ
hive 36.52% <20.00%> (+0.22%) ⬆️
mysql 55.00% <20.00%> (-0.34%) ⬇️
postgres 55.01% <20.00%> (-0.34%) ⬇️
presto 38.35% <20.00%> (+0.17%) ⬆️
python 86.44% <100.00%> (+0.30%) ⬆️
sqlite 54.74% <20.00%> (-0.33%) ⬇️
unit 79.68% <100.00%> (+0.57%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

eschutho added a commit to preset-io/agor-teammate that referenced this pull request Aug 13, 2026
@eschutho eschutho closed this Sep 5, 2026
@eschutho eschutho reopened this Sep 5, 2026
@github-actions github-actions Bot added the requires:rebase Requires rebasing on top of current master label Sep 22, 2026
@eschutho
eschutho force-pushed the fix-jinja-undefined-execute-async branch from 4a24fb5 to 176e08c Compare September 23, 2026 22:08
@github-actions github-actions Bot removed the requires:rebase Requires rebasing on top of current master label Sep 23, 2026
@bito-code-review

bito-code-review Bot commented Sep 24, 2026 •

Copy link
Copy Markdown
Contributor

Code Review Agent Run #3ac5e4

Actionable Suggestions - 0
Review Details
  • Files reviewed - 2 · Commit Range: 176e08c..176e08c
    • superset/sql/execution/executor.py
    • tests/unit_tests/sql/execution/test_executor.py
  • Files skipped - 0
  • Tools
    • MyPy (Static Code Analysis) - ✔︎ Successful
    • Astral Ruff (Static Code Analysis) - ✔︎ Successful
    • Whispers (Secret Scanner) - ✔︎ Successful
    • Detect-secrets (Secret Scanner) - ✔︎ Successful

Bito Usage Guide

Commands

Type the following command in the pull request comment and save the comment.

  • /review - Manually triggers an incremental AI Review.

  • /review full - Manually triggers a full AI Review.

  • /pause - Pauses automatic reviews on this pull request.

  • /resume - Resumes automatic reviews.

  • /resolve - Marks all Bito-posted review comments as resolved.

  • /abort - Cancels all in-progress reviews.

Refer to the documentation for additional commands.

Configuration

This repository uses Superset You can customize the agent settings here or contact your Bito workspace admin at evan@preset.io.

Documentation & Help

AI Code Review powered by Bito Logo

@github-actions github-actions Bot added the requires:rebase Requires rebasing on top of current master label Oct 2, 2026
…dError in SQLExecutor.execute_async

_render_sql_template() called process_template() with no try/except, so a
Jinja UndefinedError for an undefined variable not called as a function
would leak raw past execute_async() (execute() already caught it via its
broad except Exception). Wrap the call and re-raise as
SupersetTemplateException, consistent with how process_template() itself
already handles this failure mode elsewhere.
@eschutho
eschutho force-pushed the fix-jinja-undefined-execute-async branch from 176e08c to 5617509 Compare October 2, 2026 22:03
@github-actions github-actions Bot removed the requires:rebase Requires rebasing on top of current master label Oct 2, 2026
@bito-code-review

bito-code-review Bot commented Oct 3, 2026 •

Copy link
Copy Markdown
Contributor

Code Review Agent Run #6c4b06

Actionable Suggestions - 0
Additional Suggestions - 2
  • tests/unit_tests/sql/execution/test_executor.py - 2
    • Inline import violates rule · Line 954-954
      Inline `from superset.exceptions import SupersetTemplateException` inside the test body contradicts the repo rule that imports live at module level unless a documented circular dependency exists; none applies here. Sibling test `test_execute_async_dml_without_permission_raises` (line 937) has the same pattern, but for new code the import belongs in the top block with the other imports.
    • Weak exception assertion · Line 962-963
      `pytest.raises(SupersetTemplateException)` also passes if `execute_async` leaks a broader `SupersetException` or any subclass raised for an unrelated reason, so the test does not strictly pin the `_render_sql_template` contract it names. Adding `match=` on the undefined-variable message would make the assertion verify the actual behavior described in the docstring.
Review Details
  • Files reviewed - 2 · Commit Range: 5617509..5617509
    • superset/sql/execution/executor.py
    • tests/unit_tests/sql/execution/test_executor.py
  • Files skipped - 0
  • Tools
    • MyPy (Static Code Analysis) - ✔︎ Successful
    • Astral Ruff (Static Code Analysis) - ✔︎ Successful
    • Whispers (Secret Scanner) - ✔︎ Successful
    • Detect-secrets (Secret Scanner) - ✔︎ Successful

Bito Usage Guide

Commands

Type the following command in the pull request comment and save the comment.

  • /review - Manually triggers an incremental AI Review.

  • /review full - Manually triggers a full AI Review.

  • /pause - Pauses automatic reviews on this pull request.

  • /resume - Resumes automatic reviews.

  • /resolve - Marks all Bito-posted review comments as resolved.

  • /abort - Cancels all in-progress reviews.

Refer to the documentation for additional commands.

Configuration

This repository uses Superset You can customize the agent settings here or contact your Bito workspace admin at evan@preset.io.

Documentation & Help

AI Code Review powered by Bito Logo

@eschutho eschutho closed this Oct 3, 2026
@eschutho eschutho reopened this Oct 3, 2026

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

global:jinja Related to Jinja templating size/M sqllab Namespace | Anything related to the SQL Lab

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant