Skip to content

fix(tasks): don't re-raise validation-class errors in async chart-data cache task (SC-118140) - #43464

Open
eschutho wants to merge 3 commits into
masterfrom
fix-chartdata-query-failed-celery-noise
Open

fix(tasks): don't re-raise validation-class errors in async chart-data cache task (SC-118140)#43464
eschutho wants to merge 3 commits into
masterfrom
fix-chartdata-query-failed-celery-noise

Conversation

@eschutho

@eschutho eschutho commented Aug 24, 2026

Copy link
Copy Markdown
Member

Summary

Sentry: SUPERSET-PYTHON-13JV — 908 events / 0 users over the trailing 14 days, chronic since 2026-05-22, still actively firing.

Root cause

load_chart_data_into_cache (the Celery task backing GLOBAL_ASYNC_QUERIES chart loads) catches every exception from ChartDataCommand.run() in one generic except Exception block that reports the failure to the client via async_query_manager.update_job(...) and then unconditionally re-raises.

For ChartDataQueryFailedError — raised when a chart still references columns that have since been dropped from its dataset (customer-side schema drift, not a Superset bug) — this is inconsistent with how the codebase already treats the same exception type in the synchronous path: ChartDataRestApi._get_data_response explicitly maps ChartDataQueryFailedError to a 400 (and its sibling ChartDataCacheLoadError to 422), i.e. these are already classified elsewhere as expected, client-facing validation failures, not server bugs.

Because the async task always re-raises, Celery's default task-exception handling (and the Sentry Celery integration) captures every occurrence as an unhandled ERROR, even though update_job has already delivered a clean error to the client. This task is fire-and-forget (apply_async, no .get(), no retry policy, no result-backend consumer), so nothing depends on the task's own exception/FAILURE state for these two exception types.

Fix

Add a dedicated except (ChartDataCacheLoadError, ChartDataQueryFailedError) branch before the generic handler in superset/tasks/async_queries.py: still call update_job(..., STATUS_ERROR, ...) so client-facing behavior is unchanged, log at INFO, and don't re-raise. All other exception types — including genuinely unexpected ones and the SIP-40 SupersetErrorException/SupersetErrorsException family — are untouched and still re-raise exactly as before.

Tradeoffs

This changes failure-mode semantics for these two exception types specifically: the Celery task now completes without raising even though the underlying chart query failed (the client still sees the error via update_job/the job-status poll, unchanged). I verified no code path inspects this task's own exception/result state — it's fire-and-forget with no retry policy — so this should be safe, but flagging it explicitly since it's a real behavior change, not a pure log-level tweak.

Testing

  • ruff check / ruff format --check clean on both touched files.
  • pytest tests/unit_tests/tasks/test_async_queries.py — 9/9 passing: repointed the existing generic-error test at a plain RuntimeError (it was incidentally using ChartDataQueryFailedError as a stand-in for "some exception"), and added two new tests asserting ChartDataQueryFailedError and ChartDataCacheLoadError are reported via update_job but do not re-raise.
  • Full app-context suite unusable in this shared clone (missing superset_core, known local env issue) — ran the target file directly with superset-core added to PYTHONPATH.

Follow-ups

None identified — the fix is self-contained to this one task.

Shortcut: sc-118140

🤖 Generated with Claude Code


Update: self-review (Claude Code, Codex unavailable — platform outage) caught that the unit-test-only local verification missed an integration test (tests/integration_tests/tasks/async_queries_tests.py::test_load_chart_data_into_cache_error) still asserting the old re-raise behavior — would have failed CI. Fixed in a follow-up commit to match the new no-reraise contract; confirmed via manual trace + ruff (the integration suite itself isn't runnable in this environment, a local parameterized package gap unrelated to this change).

…a cache task (SC-118140)

ChartDataQueryFailedError/ChartDataCacheLoadError map to 400/422 in the
synchronous chart/data endpoint - expected, client-facing validation
failures (e.g. a chart referencing columns a customer has since dropped
from the dataset), not application bugs. load_chart_data_into_cache
unconditionally re-raised every exception after reporting it via
update_job, so these got double-reported: once cleanly to the client,
and again as an unhandled Celery task exception (and Sentry ERROR).

Fixes SUPERSET-PYTHON-13JV

Co-Authored-By: Claude <noreply@anthropic.com>
@dosubot dosubot Bot added change:backend Requires changing the backend global:async-query Related to Async Queries feature labels Aug 24, 2026
@bito-code-review

bito-code-review Bot commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

Code Review Agent Run #109719

Actionable Suggestions - 0
Additional Suggestions - 1
  • superset/tasks/async_queries.py - 1
    • Inconsistent error attribute access · Line 142-142
      Replace `str(ex.message)` with `str(ex)` to safely handle cases where `.message` might be absent or empty, preventing an AttributeError and ensuring consistent behavior with the fallback at line 152.
Filtered by Review Rules

Bito filtered these suggestions based on rules created automatically for your feedback. Manage rules.

  • tests/unit_tests/tasks/test_async_queries.py - 1
Review Details
  • Files reviewed - 2 · Commit Range: a690210..a690210
    • superset/tasks/async_queries.py
    • tests/unit_tests/tasks/test_async_queries.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

Comment on lines +94 to +97
mock_query_context_schema_cls.return_value.load.side_effect = err

# Should not raise.
load_chart_data_into_cache(job_metadata, form_data)

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 regression test raises ChartDataQueryFailedError from ChartDataQueryContextSchema.load, so it never executes ChartDataCommand.run() or the production logic that converts query payload errors into this exception. This can pass even if the real chart-data failure path is not caught correctly; mock ChartDataCommand.run to raise the exception after a valid query context is created, as the cache-load test does. [possible bug]

Severity Level: Minor 🧹
- ⚠️ Unit regression test does not cover `ChartDataCommand.run`.
- ⚠️ Incorrect exception handling could regress undetected.
- ⚠️ Async chart-data error behavior remains weakly protected.

Use CodeAnt Skill

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

**Path:** tests/unit_tests/tasks/test_async_queries.py
**Line:** 94:97
**Comment:**
	*Possible Bug: The regression test raises `ChartDataQueryFailedError` from `ChartDataQueryContextSchema.load`, so it never executes `ChartDataCommand.run()` or the production logic that converts query payload errors into this exception. This can pass even if the real chart-data failure path is not caught correctly; mock `ChartDataCommand.run` to raise the exception after a valid query context is created, as the cache-load test does.

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
👍 | 👎

@bito-code-review

Copy link
Copy Markdown
Contributor

The flagged issue is correct. In test_load_chart_data_into_cache_with_query_failed_error_does_not_reraise, the ChartDataQueryFailedError is raised by the schema's load method, which causes the function to exit before reaching the ChartDataCommand.run() call. To properly test the production logic, the test should mock ChartDataCommand.run to raise the exception instead, ensuring the error handling path is actually exercised.

To resolve this, update the test to mock the command class and set the side effect on its run method, similar to how test_load_chart_data_into_cache_with_cache_load_error_does_not_reraise is implemented.

tests/unit_tests/tasks/test_async_queries.py

@mock.patch("superset.tasks.async_queries.security_manager")
@mock.patch("superset.tasks.async_queries.async_query_manager")
@mock.patch("superset.commands.chart.data.get_data_command.ChartDataCommand")
@mock.patch("superset.tasks.async_queries.ChartDataQueryContextSchema")
def test_load_chart_data_into_cache_with_query_failed_error_does_not_reraise(
    mock_query_context_schema_cls, mock_command_cls, mock_async_query_manager, mock_security_manager
):
    # ... setup ...
    mock_query_context_schema_cls.return_value.load.return_value = mock.MagicMock()
    mock_command_cls.return_value.run.side_effect = ChartDataQueryFailedError(_(err_message))
    # ... execution and assertion ...

@eschutho
eschutho requested a review from richardfogaca August 24, 2026 15:23
eschutho and others added 2 commits August 24, 2026 15:25
…r behavior (SC-118140)

Self-review caught that the unit-test-only local verification missed
tests/integration_tests/tasks/async_queries_tests.py::test_load_chart_data_into_cache_error,
which still asserted the old re-raise behavior via pytest.raises(...) -
would have failed CI. Updated it to match the new no-reraise contract
(load_chart_data_into_cache no longer raises for this exception type,
still reports it via update_job).

Co-Authored-By: Claude <noreply@anthropic.com>
…reraise test (SC-118140)

Automated PR review (bito-code-review) correctly flagged that this test
mocked the schema loader's side effect instead of ChartDataCommand.run,
so it never actually reached the run() call the exception is meant to
simulate failing at. The except clause still catches the exception either
way (same try block), so the assertion was never wrong, but mocking at
the real trigger point matches the sibling ChartDataCacheLoadError test
and the corrected integration test, and is more representative of the
actual failure path.

Co-Authored-By: Claude <noreply@anthropic.com>
@pull-request-size pull-request-size Bot added size/L and removed size/M labels Aug 24, 2026
@netlify

netlify Bot commented Aug 24, 2026

Copy link
Copy Markdown

Deploy Preview for superset-docs-preview ready!

Name Link
🔨 Latest commit 9557995
🔍 Latest deploy log https://app.netlify.com/projects/superset-docs-preview/deploys/6a8c6282b1069800080a467c
😎 Deploy Preview https://deploy-preview-43464--superset-docs-preview.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

@netlify

netlify Bot commented Aug 24, 2026

Copy link
Copy Markdown

Deploy Preview for superset-docs-preview ready!

Name Link
🔨 Latest commit 29f9905
🔍 Latest deploy log https://app.netlify.com/projects/superset-docs-preview/deploys/6a8c62e4582cff0008c1d41d
😎 Deploy Preview https://deploy-preview-43464--superset-docs-preview.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

@codecov

codecov Bot commented Aug 24, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 78.85%. Comparing base (8fa48d7) to head (29f9905).
⚠️ Report is 9 commits behind head on master.

Additional details and impacted files
@@            Coverage Diff             @@
##           master   #43464      +/-   ##
==========================================
- Coverage   78.85%   78.85%   -0.01%     
==========================================
  Files        2876     2876              
  Lines      164601   164605       +4     
  Branches    38015    38015              
==========================================
+ Hits       129799   129801       +2     
- Misses      32355    32357       +2     
  Partials     2447     2447              
Flag Coverage Δ
hive 38.07% <0.00%> (-0.01%) ⬇️
mysql 57.79% <100.00%> (-0.01%) ⬇️
postgres 57.83% <100.00%> (-0.01%) ⬇️
presto 40.00% <0.00%> (-0.01%) ⬇️
python 83.54% <100.00%> (-0.01%) ⬇️
sqlite 57.51% <100.00%> (-0.01%) ⬇️
unit 73.55% <100.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.

@bito-code-review

bito-code-review Bot commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

Code Review Agent Run #209ddf

Actionable Suggestions - 0
Review Details
  • Files reviewed - 2 · Commit Range: a690210..29f9905
    • tests/integration_tests/tasks/async_queries_tests.py
    • tests/unit_tests/tasks/test_async_queries.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

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 global:async-query Related to Async Queries feature preset-io size/L

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant