{"record":{"id":"27dd83d114781bb9","repo":"apache/superset","slug":"the-chart-this-report-targets-was-deleted-restore","errorCode":null,"errorMessage":"The chart this report targets was deleted. Restore the chart, or update the report to point at an active chart.","messagePattern":"The chart this report targets was deleted\\. Restore the chart, or update the report to point at an active chart\\.","errorType":"exception","errorClass":"ReportScheduleTargetChartDeletedError","httpStatus":500,"severity":"error","filePath":"superset/commands/report/execute.py","lineNumber":498,"sourceCode":"        Get the url for this report schedule: chart or dashboard\n        \"\"\"\n        chart = self._report_schedule.chart\n        dashboard = self._report_schedule.dashboard\n\n        # Soft delete removed the FK-level guarantee that a report's target\n        # chart exists: ``chart`` is a visibility-filtered relationship, so a\n        # chart soft-deleted after this report was created (or attached via a\n        # validate/commit race with DeleteChartCommand) loads as ``None``\n        # while ``chart_id`` is still set. Without this guard the branch\n        # below silently falls through to the dashboard path and fails\n        # opaquely; raising here surfaces a clear, actionable error inside\n        # the state-machine envelope (ERROR log row + notification dispatch).\n        # Every content path (_get_screenshots, _get_csv_data,\n        # _get_embedded_data, _get_notification_content) funnels through this\n        # method, so this is the single choke point.\n        if chart is None and dashboard is None:\n            if self._report_schedule.chart_id is not None:\n                raise ReportScheduleTargetChartDeletedError()\n            # Symmetric guard for dashboard targets. Dashboard soft delete lands\n            # in the sibling rollout; until then this cannot fire (a dashboard\n            # with dependent reports cannot be deleted), which makes it inert\n            # rather than wrong — and it keeps the report-target error vocabulary\n            # parallel across entities from day one.\n            if self._report_schedule.dashboard_id is not None:\n                raise ReportScheduleTargetDashboardDeletedError()\n            # Defensive fallback for a malformed report with no target IDs.\n            # Missing relationships with a target ID are handled by the\n            # dedicated deleted-target errors above.\n            raise ReportScheduleUnexpectedError(\n                f\"Report schedule {self._report_schedule.id} \"\n                f\"({self._report_schedule.name!r}) has no resolvable target \"\n                f\"(chart_id={self._report_schedule.chart_id}, \"\n                f\"dashboard_id={self._report_schedule.dashboard_id}); \"\n                \"the report has neither a chart nor a dashboard.\"\n            )\n","sourceCodeStart":480,"sourceCodeEnd":516,"githubUrl":"https://github.com/apache/superset/blob/f4587218dd19d046c3e4d00063e7d27f8a2ed354/superset/commands/report/execute.py#L480-L516","documentation":"ReportScheduleTargetChartDeletedError: ReportSchedule.chart is a visibility-filtered relationship, so a chart soft-deleted after the report was created loads as None while chart_id stays set. The _get_url choke point detects chart is None with chart_id set and raises this actionable error instead of silently falling through to the dashboard path and failing opaquely. It lands inside the state-machine envelope (ERROR log row plus notification dispatch).","triggerScenarios":"Report targets a chart that is later soft-deleted (trash); a validate/commit race with DeleteChartCommand removes the chart between report creation and execution; the next scheduled run calls _get_url/_get_screenshots/_get_csv_data and hits the guard.","commonSituations":"Chart cleanup sweeps that soft-delete unused charts without checking dependent reports; users deleting a chart from the chart list not realizing a report/alert points at it.","solutions":["Restore the chart from trash (undelete) so the relationship resolves again","Or edit the report to target an active chart / dashboard","Going forward, check dependents before deleting charts (the delete API reports which reports reference a chart)"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"from superset.commands.report.exceptions import ReportScheduleTargetChartDeletedError\n\ndef report_target_ok(report) -> bool:\n    return report.chart_id is None or report.chart is not None","typeGuard":"def has_live_chart_target(report_schedule) -> bool:\n    \"\"\"True when the report either has no chart target or the chart is not soft-deleted.\"\"\"\n    return report_schedule.chart_id is None or report_schedule.chart is not None","tryCatchPattern":"try:\n    _get_url()\nexcept ReportScheduleTargetChartDeletedError:\n    # notify owner: restore chart from trash or retarget the report\n    notify_owner(report_schedule, 'target chart deleted')","preventionTips":["Check a chart's dependent reports before soft-deleting it","Monitor execution logs for deleted-target errors to catch orphaned reports early","Handle chart and dashboard deleted-target errors in one shared except clause"],"tags":["alerts-reports","soft-delete","charts","execution"],"backgroundTag":null,"analyzedSha":"f4587218dd19d046c3e4d00063e7d27f8a2ed354","analyzedAt":"2026-08-14T22:39:27.425Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}