Skip to content

fix(sqllab): wrap process_template() in QueryEstimationCommand to prevent raw UndefinedError leak - #42757

Merged
eschutho merged 1 commit into
masterfrom
fix-jinja-undefined-error-estimate
Aug 4, 2026
Merged

fix(sqllab): wrap process_template() in QueryEstimationCommand to prevent raw UndefinedError leak#42757
eschutho merged 1 commit into
masterfrom
fix-jinja-undefined-error-estimate

Conversation

@eschutho

@eschutho eschutho commented Aug 4, 2026

Copy link
Copy Markdown
Member

SUMMARY

QueryEstimationCommand.run() (superset/commands/sql_lab/estimate.py, the command behind SQL Lab's "estimate query cost" feature) calls template_processor.process_template() with no try/except around it. process_template() (superset/jinja_context.py) normally converts Jinja errors into typed Superset exceptions (SupersetSyntaxErrorException, SupersetTemplateException, UndefinedTemplateFunctionException) — but it has one bare-raise fallback for jinja2.exceptions.UndefinedError when an undefined template variable is accessed via attribute/subscript (e.g. {{ foo.bar }}) rather than called as a function. That raw exception propagates past estimate.py's unguarded call site, past the API layer (superset/sqllab/api.py::estimate_query_cost, /api/v1/sqllab/estimate/), which also has no try/except around command.run(), and lands on Flask's global catch-all handler — an opaque 500 instead of a typed 4xx, for what's just a malformed Jinja template typed into the SQL Lab editor.

The sibling command in the same package, ExecuteSqlCommand.run() (superset/commands/sql_lab/execute.py), already guards against this class of leak by wrapping its whole body and converting any non-Superset exception into a typed one. estimate.py had no equivalent guard for this call site.

Same bug class as #42366, #42401, #42426, #42442, #42714 — a prior daily-cleanup pipeline that's been converting these raw-exception leaks into properly typed/statused Superset exceptions across the codebase.

FIX

Wraps only the process_template() call in except TemplateError (jinja2's own base exception, not Superset's), converting the leak into SupersetErrorException(status=400). This is additive-only: SupersetSyntaxErrorException/SupersetTemplateException are Superset's own exception hierarchy, not TemplateError subclasses, so their existing (already-correct) propagation paths are untouched.

TESTING INSTRUCTIONS

  • New regression test test_run_wraps_raw_jinja_undefined_error in tests/unit_tests/commands/sql_lab/test_estimate.py: mocks get_template_processor().process_template to raise a raw jinja2.exceptions.UndefinedError, calls QueryEstimationCommand.run(), asserts a SupersetErrorException with status == 400 is raised instead.
  • Confirmed the test fails on pre-fix code (raw UndefinedError propagates uncaught) and passes post-fix.
  • Manual repro: in SQL Lab, estimate the cost of a query with a template like SELECT {{ foo.bar }} where foo is not defined in the Jinja context — pre-fix this 500s with a generic error; post-fix it returns a 400 with the Jinja error message.
  • ruff check / ruff format --check clean on both changed files (pinned 0.9.7 via uvx).

ADDITIONAL INFORMATION

  • Has associated issue:
  • Required feature flags:
  • Changes UI
  • Includes DB Migration (follow approval process in SIP-59)
    • Migration is atomic, supports rollback & is backwards-compatible
    • Confirm DB migration upgrade and downgrade tested
    • Runtime estimates and downtime expectations provided
  • Introduces new feature or API
  • Removes existing feature or API

Tradeoffs: additive-only — no existing failure-mode semantics change. The one previously-uncaught path (raw UndefinedError for undefined attribute/subscript access in an estimate-time template) now returns a typed 400 instead of an opaque 500; no other paths are touched.

Correction for accuracy: the except TemplateError clause is slightly broader than the illustrative example above suggests. SparkTemplateProcessor/TrinoTemplateProcessor (jinja_context.py) override process_template() entirely with no internal try/except of their own, so for those two engines any TemplateError (not just the attribute/subscript UndefinedError case) was previously leaking raw and is now also caught and converted to a typed 400. This is strictly the same failure mode at the same call site (raw 500-leak → typed 400), just triggered by more TemplateError subtypes/engines than the one example covers — not a new or different behavior change.

…vent raw UndefinedError leak

BaseTemplateProcessor.process_template() has a bare `raise` fallback for
jinja2 UndefinedError on undefined attribute/subscript access (e.g.
{{ foo.bar }}) that doesn't match its "is it a function call" heuristic.
QueryEstimationCommand.run() called process_template() with no try/except
around it, so this raw jinja2.exceptions.UndefinedError propagated past the
API layer and hit Flask's catch-all handler, producing an opaque 500 instead
of a typed 4xx error for a bad Jinja template typed into SQL Lab's cost
estimator.

Wrap the call in a try/except TemplateError and convert it into a
SupersetErrorException(status=400), matching the broad-catch pattern
ExecuteSqlCommand already uses for the same class of leak in the sibling
execute.py command.
@dosubot dosubot Bot added global:jinja Related to Jinja templating sqllab Namespace | Anything related to the SQL Lab labels Aug 4, 2026
@bito-code-review

bito-code-review Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Code Review Agent Run #be2372

Actionable Suggestions - 0
Review Details
  • Files reviewed - 2 · Commit Range: 98156b4..98156b4
    • superset/commands/sql_lab/estimate.py
    • tests/unit_tests/commands/sql_lab/test_estimate.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

@codecov

codecov Bot commented Aug 4, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 60.00000% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 65.57%. Comparing base (3dde95d) to head (98156b4).
⚠️ Report is 1 commits behind head on master.

Files with missing lines Patch % Lines
superset/commands/sql_lab/estimate.py 60.00% 2 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master   #42757      +/-   ##
==========================================
- Coverage   65.59%   65.57%   -0.02%     
==========================================
  Files        2819     2819              
  Lines      160372   160376       +4     
  Branches    36617    36617              
==========================================
- Hits       105188   105162      -26     
- Misses      53120    53147      +27     
- Partials     2064     2067       +3     
Flag Coverage Δ
hive 38.07% <20.00%> (-0.01%) ⬇️
mysql ?
postgres 57.96% <60.00%> (-0.01%) ⬇️
presto 39.99% <20.00%> (-0.01%) ⬇️
python 59.30% <60.00%> (-0.04%) ⬇️
sqlite 57.59% <60.00%> (-0.01%) ⬇️
unit 100.00% <ø> (ø)

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.

@sadpandajoe sadpandajoe added the review:checkpoint Last PR reviewed during the daily review standup label Aug 4, 2026
@eschutho
eschutho merged commit 4e9e884 into master Aug 4, 2026
85 checks passed
@eschutho
eschutho deleted the fix-jinja-undefined-error-estimate branch August 4, 2026 22:02
@sadpandajoe sadpandajoe removed the review:checkpoint Last PR reviewed during the daily review standup label Aug 6, 2026
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 preset-io size/M sqllab Namespace | Anything related to the SQL Lab

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants