Skip to content

fix(sqla): move process_template inside try block in get_fetch_values_predicate - #44246

Merged
eschutho merged 1 commit into
masterfrom
fix-fetch-values-predicate-jinja-undefinederror
Sep 14, 2026
Merged

eschutho merged 1 commit into
masterfrom
fix-fetch-values-predicate-jinja-undefinederror

Conversation

@eschutho

Copy link
Copy Markdown
Member

SUMMARY

Problem: In SqlaTable.get_fetch_values_predicate, the template_processor.process_template() call sits outside the try: block whose except (TemplateError, SupersetSyntaxErrorException) clause was written to catch Jinja template errors from it. A bare jinja2.exceptions.UndefinedError (a TemplateError subclass) raised by attribute access on an undefined variable (e.g. {{ foo.bar }}) escapes uncaught, propagating as an opaque 500 (GENERIC_BACKEND_ERROR) instead of the intended 400 with a helpful "Error in jinja expression in fetch values predicate" message.

Fix: Move the process_template call inside the existing try: block so the pre-existing except clause actually covers it. No new except clauses, no refactoring — just correcting the scope of the existing handler.

Precedent: This is the same bug class as the fix in #42401 (catch TemplateError alongside SupersetSyntaxErrorException in models.py) — UndefinedError escaping process_template due to misscoped or missing error handling. The existing test test_get_dataset_include_rendered_sql_handles_undefined_error covers the analogous fix in the datasets/api.py call path; this PR covers the get_fetch_values_predicate call path.

BEFORE/AFTER SCREENSHOTS OR ANIMATED GIF

N/A — backend-only error handling fix.

TESTING INSTRUCTIONS

  1. New unit test test_get_fetch_values_predicate_wraps_undefined_error in tests/unit_tests/connectors/sqla/models_test.py:
    • Mocks process_template to raise UndefinedError("'foo' is undefined")
    • Asserts that get_fetch_values_predicate wraps it in QueryObjectValidationError
    • Verified fail-before/pass-after: test fails on unmodified code (raw UndefinedError escapes), passes after the fix
  2. Full test file (85 tests) passes with no regressions
  3. ruff check, ruff format --check, and mypy all pass

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

…_predicate

The process_template call was outside the try block that contains the
except (TemplateError, SupersetSyntaxErrorException) handler written
to catch it. A bare jinja2.exceptions.UndefinedError (e.g. from
{{ foo.bar }} attribute access on an undefined variable) would escape
uncaught, producing an opaque 500 instead of a clean 400 with the
intended "Error in jinja expression in fetch values predicate" message.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@bito-code-review

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

Copy link
Copy Markdown
Contributor

Code Review Agent Run #420c8c

Actionable Suggestions - 0
Review Details
  • Files reviewed - 2 · Commit Range: 5abb8eb..5abb8eb
    • superset/connectors/sqla/models.py
    • tests/unit_tests/connectors/sqla/models_test.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

@codecov

codecov Bot commented Sep 14, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 50.00000% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 80.18%. Comparing base (d028acd) to head (5abb8eb).
⚠️ Report is 9 commits behind head on master.

Files with missing lines Patch % Lines
superset/connectors/sqla/models.py 50.00% 0 Missing and 1 partial ⚠️
Additional details and impacted files
@@           Coverage Diff           @@
##           master   #44246   +/-   ##
=======================================
  Coverage   80.17%   80.18%           
=======================================
  Files        2925     2925           
  Lines      172696   172696           
  Branches    40092    40092           
=======================================
+ Hits       138467   138472    +5     
+ Misses      31631    31625    -6     
- Partials     2598     2599    +1     
Flag Coverage Δ
hive 37.33% <0.00%> (ø)
mysql 56.85% <0.00%> (ø)
postgres 56.88% <0.00%> (ø)
presto 39.21% <0.00%> (ø)
python 84.57% <50.00%> (+<0.01%) ⬆️
sqlite 56.58% <0.00%> (ø)
unit 75.91% <50.00%> (+<0.01%) ⬆️

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.

@rebenitez1802 rebenitez1802 left a comment

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.

Approve — correct, minimal, well-scoped fix; security model intact and no regressions. The core mechanism checks out: {{ foo.bar }} raises a raw jinja2 UndefinedError, which is a TemplateError subclass (MRO: UndefinedError → TemplateRuntimeError → TemplateError), so moving process_template inside the try lets the existing except (TemplateError, SupersetSyntaxErrorException) clause wrap it into a 400 instead of leaking a 500. TemplateError is already imported and getattr(ex, "message", …) resolves correctly for UndefinedError. Only Low-severity, optional suggestions below — none blocking.

🟢 Low — Test pins the exception type but not the helpful message the fix delivers

test_get_fetch_values_predicate_wraps_undefined_error uses a bare pytest.raises(QueryObjectValidationError). Since the PR's whole purpose is a 400 carrying "Error in jinja expression in fetch values predicate: 'foo' is undefined", a later change that garbles getattr(ex, "message", str(ex)), edits the clause text, or routes the error through the first (…failed SQL validation) clause would keep the test green. The sibling test this PR cites, test_get_dataset_include_rendered_sql_handles_undefined_error, does assert its wrapped message, so this is worth matching. The core scoping fix itself is protected (reverting the move surfaces an uncaught UndefinedError and fails this test) — so this is message-quality hardening only. Fix: with pytest.raises(QueryObjectValidationError, match="Error in jinja expression in fetch values predicate"): and bind as exc to also assert "'foo' is undefined" in str(exc.value).

🟢 Low — Sibling render paths still surface the same jinja error as an opaque 500 (pre-existing, optional follow-up)

The same bug class lives in four adjacent query-time render sites that catch only SupersetSyntaxErrorException: TableColumn.get_sqla_col, get_timestamp_expression, SqlMetric.get_sqla_col, and _render_adhoc_expression_for_metadata_lookup. A calculated column/metric containing {{ foo.bar }} raises a raw UndefinedError there too, which isn't a SupersetSyntaxErrorException, so it escapes to a 500 — exactly what this PR fixes for fetch-values. Not touched or introduced by this diff, so not blocking; consider broadening those clauses to (TemplateError, SupersetSyntaxErrorException) here or in a follow-up, for consistency with this fix, the RLS path, and datasets/api.py.

🟢 Low — Undefined function calls ({{ foo() }}) still return 422, not the new 400 (pre-existing edge, optional)

An undefined function call raises UndefinedTemplateFunctionException (a SupersetTemplateException, status 422), which is neither TemplateError nor SupersetSyntaxErrorException, so it bypasses the new wrapper — the user gets a 422 with a bare message rather than the contextual 400 the undefined-variable case now produces. Behavior is identical before and after this diff (not a regression), and 422 is already a meaningful typed error rather than an opaque 500, so this is only a minor UX inconsistency versus the case being fixed. If you want them aligned, add SupersetTemplateException to the second except tuple (its .message works with the existing getattr).

@eschutho
eschutho merged commit 6aaa3ed into master Sep 14, 2026
81 checks passed
@eschutho
eschutho deleted the fix-fetch-values-predicate-jinja-undefinederror branch September 14, 2026 22:03
villebro pushed a commit that referenced this pull request Sep 16, 2026
…_predicate (#44246)

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
(cherry picked from commit 6aaa3ed)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants