Skip to content

fix(reports): fail closed on alert screenshot capture instead of delivering a blank - #43031

Draft
eschutho wants to merge 1 commit into
masterfrom
alerts-fail-closed-screenshot
Draft

fix(reports): fail closed on alert screenshot capture instead of delivering a blank#43031
eschutho wants to merge 1 commit into
masterfrom
alerts-fail-closed-screenshot

Conversation

@eschutho

Copy link
Copy Markdown
Member

SUMMARY

Scheduled alerts that attach a dashboard/chart screenshot (PNG/PDF) could deliver a blank or partial image on large or slow-rendering dashboards, while scheduled reports were already protected.

Root cause. AsyncExecuteReportScheduleCommand.run() built a ReportExecutionContext only when type == REPORT. Alerts ran with report_execution_context=None, and in superset/utils/screenshot_utils.py that None:

  • selects the lenient readiness predicate (CHART_HOLDERS_READY_JS) instead of the fail-closed REPORT_CHART_HOLDERS_READY_JS, and
  • sets allow_partial_fallback=True,

so partial/blank tiles were combined and delivered instead of failing.

Fix. Route alerts that deliver a rendered screenshot (PNG/PDF with ALERTS_ATTACH_REPORTS enabled) through the same execution context reports use, so a blank/incomplete capture raises ReportScheduleScreenshotFailedError and hands off to the existing retry/notification and error-handling machinery. The type == REPORT discriminator is replaced with an explicit "delivers a rendered artifact" check (_should_build_execution_context).

Deliberately left unchanged / lenient: CSV/text alerts, alerts without the attach flag, the non-delivered query-context capture (used to force a chart's saved query context), and UI thumbnails. Report behavior is unchanged (reports still always run under a context, regardless of format).

Secondary fix. ReportSuccessState (the Success/Grace path) logged ERROR on a delivery failure but did not notify the owner, unlike the first-run ReportNotTriggeredErrorState path which calls send_error(). A schedule whose previous run succeeded and then failed to deliver would fail silently — the common case once screenshots fail closed. The Success/Grace path now sends the owner error notification (respecting the error grace period), mirroring the first-run path.

BEFORE/AFTER SCREENSHOTS OR ANIMATED GIF

Not applicable (no UI change).

TESTING INSTRUCTIONS

Automated (unit):

  • _should_build_execution_context is True for all reports and for PNG/PDF alerts when ALERTS_ATTACH_REPORTS is enabled; False for CSV/text/xlsx alerts and for alerts with the flag off — see test_should_build_execution_context.
  • The Success/Grace path notifies the owner on a delivery failure and respects the error grace period — see test_success_state_send_failure_notifies_owner and test_success_state_send_failure_skips_notification_in_error_grace.
  • The delivered-screenshot fail-open vs fail-closed contract at the tile-combine layer is already covered in tests/unit_tests/utils/test_screenshot_utils.py.

Run:

pytest tests/unit_tests/commands/report/ \
       tests/unit_tests/utils/test_screenshot_utils.py \
       tests/unit_tests/utils/test_report_execution.py \
       tests/unit_tests/reports/

Manual: configure an alert with a PNG/PDF screenshot attachment (ALERTS_ATTACH_REPORTS on) against a large/slow dashboard. Instead of emailing a blank image, the execution now fails closed and follows the schedule's configured retry/error-notification behavior.

ADDITIONAL INFORMATION

  • Has associated issue:
  • Required feature flags: ALERTS_ATTACH_REPORTS (governs whether alerts attach a rendered screenshot; the fail-closed path applies only when it is enabled)
  • 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

…vering a blank

Scheduled alerts that attach a dashboard/chart screenshot (PNG/PDF) could
deliver a blank or partial image on large or slow-rendering dashboards,
while reports were protected.

Root cause: `AsyncExecuteReportScheduleCommand.run()` built a
`ReportExecutionContext` only for `type == REPORT`. Alerts ran with
`report_execution_context=None`, which in `take_tiled_screenshot` selects
the lenient readiness predicate (`CHART_HOLDERS_READY_JS` instead of
`REPORT_CHART_HOLDERS_READY_JS`) and sets `allow_partial_fallback=True`,
so partial/blank tiles were combined and delivered instead of failing.

Fix: route alerts that deliver a rendered screenshot (PNG/PDF with
`ALERTS_ATTACH_REPORTS` enabled) through the same execution context reports
use, so a blank/incomplete capture raises
`ReportScheduleScreenshotFailedError` and hands off to the existing
retry/notification and error handling. The `type == REPORT` discriminator
is replaced with an explicit "delivers a rendered artifact" check
(`_should_build_execution_context`). CSV/text alerts, alerts without the
attach flag, the non-delivered query-context capture, and UI thumbnails
keep their existing lenient behavior; report behavior is unchanged.

Also fix a notification asymmetry: `ReportSuccessState` (Success/Grace
path) logged ERROR on a send failure but did not notify the owner, unlike
the first-run `ReportNotTriggeredErrorState` path. A schedule whose
previous run succeeded then failed to deliver would fail silently — the
common case once screenshots fail closed. The Success/Grace path now sends
the owner error notification (respecting the error grace period),
mirroring the first-run path.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: Elizabeth Thompson <eschutho@gmail.com>
@netlify

netlify Bot commented Aug 11, 2026

Copy link
Copy Markdown

Deploy Preview for superset-docs-preview ready!

Name Link
🔨 Latest commit 4c5f317
🔍 Latest deploy log https://app.netlify.com/projects/superset-docs-preview/deploys/6a7a793302586a0008366f8d
😎 Deploy Preview https://deploy-preview-43031--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 11, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 31.81818% with 15 lines in your changes missing coverage. Please review.
✅ Project coverage is 66.54%. Comparing base (bfa1e77) to head (4c5f317).
⚠️ Report is 1 commits behind head on master.

Files with missing lines Patch % Lines
superset/commands/report/execute.py 31.81% 14 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master   #43031      +/-   ##
==========================================
- Coverage   66.59%   66.54%   -0.06%     
==========================================
  Files        2863     2864       +1     
  Lines      161734   161900     +166     
  Branches    37262    37301      +39     
==========================================
+ Hits       107712   107739      +27     
- Misses      51977    52116     +139     
  Partials     2045     2045              
Flag Coverage Δ
hive 38.15% <4.54%> (-0.05%) ⬇️
mysql 57.67% <31.81%> (-0.09%) ⬇️
postgres 57.73% <31.81%> (-0.09%) ⬇️
presto 40.11% <4.54%> (-0.06%) ⬇️
python 59.11% <31.81%> (-0.10%) ⬇️
sqlite 57.34% <31.81%> (-0.09%) ⬇️
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.

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.

1 participant