Skip to content

fix(sqllab): explain dataset save failures instead of "Fatal error" - #43334

Open
EnxDev wants to merge 2 commits into
masterfrom
enxdev/fix/sql-lab-save-dataset
Open

fix(sqllab): explain dataset save failures instead of "Fatal error"#43334
EnxDev wants to merge 2 commits into
masterfrom
enxdev/fix/sql-lab-save-dataset

Conversation

@EnxDev

@EnxDev EnxDev commented Aug 19, 2026

Copy link
Copy Markdown
Contributor

SUMMARY

Saving a SQL Lab query as a dataset surfaced a red Fatal error toast with no detail, whatever the actual problem was; invalid SQL, a missing table, no permissions.

CreateDatasetCommand.run() introspects the new dataset's columns by executing its SQL against the analytics database.
For a user with blanket database access that is the first time the query is parsed at all, since validate()'s parse only runs on the per-table authorization branch. Failures there raise SupersetGenericDBErrorException, which is not a SQLAlchemyError, so on_error re-raised it untouched, DatasetRestApi.post had no matching except, and it reached FAB's @safe as a bare 500 {"message": "Fatal error"}; "Fatal error" being FAB's hardcoded generic 500 string, which the frontend then showed verbatim.

Two changes, covering different failures:

1. The save now fails with a usable message. CreateDatasetCommand converts metadata-fetch failures into a DatasetInvalidError (422) carrying the engine's own text:

SQL Response
SELECT ... 422 {"sql": ["Invalid SQL: Error parsing near '.' at line 1:8"]}
SELECT * FROM no_such_table 422 {"sql": ["no such table: no_such_table"]}
SELECT no_such_col FROM birth_names 422 {"sql": ["no such column: no_such_col"]}
DROP TABLE birth_names 422 {"sql": ["Only \SELECT` statements are allowed"]}`

The transaction still rolls back, so a failed save leaves no half-built dataset behind.

2. "Save dataset" is disabled until the query has run successfully, with the same tooltip wording used by "Schedule query"; mirroring the fix direction agreed for the sibling issue.

The gate alone is not enough. It only knows whether a query ran, not whether the editor's current text is what ran: run SELECT 1, edit to SELECT * FROM no_such_table without re-running, and the button stays enabled.
Same for a table dropped between run and save, revoked permissions, or Jinja that renders differently at save time. Those all still reach the backend, which is why both halves ship together.

Also fixes a silent failure in SaveDatasetModal: the error toast in the catch was constructed but never dispatched, so a failure in the chart-payload step showed the user nothing at all.

Note on [str(ex.message)]: these messages are built with lazy_gettext, and marshmallow only auto-wraps str/bytes.
A bare LazyString serialized as {"sql": "..."} instead of {"sql": ["..."]}, and the client reads Object.values(message)[0][0] ; so the toast would have read I.

BEFORE/AFTER SCREENSHOTS OR ANIMATED GIF

1-repro-before-after.mp4
repro-before repro-after

TESTING INSTRUCTIONS

Manual, in SQL Lab against any database:

  1. Type SELECT ... in a fresh tab and do not run it. The "Save dataset" toolbar button is disabled; hovering it explains why. ("Save query" stays enabled; saving a query needs no results.)
  2. Run SELECT 1 AS a successfully, then edit the SQL to SELECT * FROM no_such_table without re-running. "Save dataset" is enabled. Save as new → Save & Explore. The toast reads no such table: no_such_table and the modal stays open so you can fix and retry.
  3. Run a valid query and save it as a dataset; the normal flow is unchanged and still opens Explore.
  4. Confirm no dataset row is created for the failed attempts.

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

@dosubot dosubot Bot added change:backend Requires changing the backend change:frontend Requires changing the frontend sqllab Namespace | Anything related to the SQL Lab labels Aug 19, 2026
@bito-code-review

bito-code-review Bot commented Aug 19, 2026

Copy link
Copy Markdown
Contributor

Code Review Agent Run #8602ca

Actionable Suggestions - 0
Review Details
  • Files reviewed - 9 · Commit Range: aae997e..aae997e
    • superset-frontend/src/SqlLab/components/SaveDatasetActionButton/SaveDatasetActionButton.test.tsx
    • superset-frontend/src/SqlLab/components/SaveDatasetActionButton/index.tsx
    • superset-frontend/src/SqlLab/components/SaveDatasetModal/SaveDatasetModal.test.tsx
    • superset-frontend/src/SqlLab/components/SaveDatasetModal/index.tsx
    • superset-frontend/src/SqlLab/components/SaveQuery/SaveQuery.test.tsx
    • superset-frontend/src/SqlLab/components/SaveQuery/index.tsx
    • superset/commands/dataset/create.py
    • tests/unit_tests/commands/dataset/test_create.py
    • tests/unit_tests/datasets/api_tests.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 19, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 83.33333% with 3 lines in your changes missing coverage. Please review.
✅ Project coverage is 66.67%. Comparing base (097c99b) to head (540f8cb).

Files with missing lines Patch % Lines
superset/commands/dataset/create.py 60.00% 2 Missing ⚠️
...d/src/SqlLab/components/SaveDatasetModal/index.tsx 83.33% 1 Missing ⚠️
Additional details and impacted files
@@           Coverage Diff           @@
##           master   #43334   +/-   ##
=======================================
  Coverage   66.67%   66.67%           
=======================================
  Files        2876     2876           
  Lines      164007   164021   +14     
  Branches    37834    37842    +8     
=======================================
+ Hits       109347   109357   +10     
- Misses      52514    52517    +3     
- Partials     2146     2147    +1     
Flag Coverage Δ
hive 38.10% <20.00%> (-0.01%) ⬇️
javascript 73.91% <92.30%> (+<0.01%) ⬆️
mysql 57.75% <60.00%> (-0.01%) ⬇️
postgres 57.78% <60.00%> (-0.01%) ⬇️
presto 40.04% <20.00%> (-0.01%) ⬇️
python 59.17% <60.00%> (-0.01%) ⬇️
sqlite 57.42% <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.

Comment thread superset-frontend/src/SqlLab/components/SaveQuery/index.tsx Outdated
@bito-code-review

Copy link
Copy Markdown
Contributor

The flagged issue is correct. The current implementation relies solely on latestQueryId to determine if the dataset button should be enabled. If a user runs a query successfully, then modifies the SQL in the editor, the latestQueryId still points to the previous successful query, causing the button to remain enabled even though the current SQL has not been executed.

To resolve this, you should invalidate the successful-run state when the SQL changes. You can achieve this by tracking the SQL string associated with the successful query and comparing it to the current editor SQL, or by resetting the latestQueryId (or a derived hasSuccessfulQuery flag) whenever the editor content changes.

Here is a concise implementation to invalidate the state when the SQL changes:

// In superset-frontend/src/SqlLab/components/SaveQuery/index.tsx

// 1. Track the SQL of the last successful run
const [lastSuccessfulSql, setLastSuccessfulSql] = useState<string | null>(null);

// 2. Update the tracker when the query state becomes success
useEffect(() => {
  if (latestQueryState === QueryState.Success) {
    setLastSuccessfulSql(queryEditor.sql);
  }
}, [latestQueryState, queryEditor.sql]);

// 3. Update the success check to verify the SQL matches
const hasSuccessfulQuery = 
  latestQueryState === QueryState.Success && 
  lastSuccessfulSql === queryEditor.sql;

I have checked the PR comments, and there are no other comments in the provided context. Would you like me to perform any further analysis or address other parts of the PR?

superset-frontend/src/SqlLab/components/SaveQuery/index.tsx

const [lastSuccessfulSql, setLastSuccessfulSql] = useState<string | null>(null);

useEffect(() => {
  if (latestQueryState === QueryState.Success) {
    setLastSuccessfulSql(queryEditor.sql);
  }
}, [latestQueryState, queryEditor.sql]);

const hasSuccessfulQuery = 
  latestQueryState === QueryState.Success && 
  lastSuccessfulSql === queryEditor.sql;

@bito-code-review

bito-code-review Bot commented Aug 19, 2026

Copy link
Copy Markdown
Contributor

Code Review Agent Run #c10eac

Actionable Suggestions - 0
Review Details
  • Files reviewed - 5 · Commit Range: aae997e..540f8cb
    • superset-frontend/src/SqlLab/components/SaveDatasetModal/SaveDatasetModal.test.tsx
    • superset-frontend/src/SqlLab/components/SaveQuery/SaveQuery.test.tsx
    • superset-frontend/src/SqlLab/components/SaveQuery/index.tsx
    • superset/commands/dataset/create.py
    • tests/unit_tests/datasets/api_tests.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

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

Labels

change:backend Requires changing the backend change:frontend Requires changing the frontend size/L sqllab Namespace | Anything related to the SQL Lab

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant