apache/superset · error · ReportScheduleUnexpectedError

Report schedule {id} ({name!r}) has no resolvable target (ch

Error message

Report schedule {id} ({name!r}) has no resolvable target (chart_id={chart_id}, dashboard_id={dashboard_id}); the report has neither a chart nor a dashboard.

What it means

ReportScheduleUnexpectedError raised as a defensive fallback in _get_url when both chart and dashboard relationships are None AND neither chart_id nor dashboard_id is set — i.e. a malformed report row with no target at all. The message embeds the schedule id, name, and both null target ids so operators can identify the row directly.

Source

Thrown at superset/commands/report/execute.py:509

        # opaquely; raising here surfaces a clear, actionable error inside
        # the state-machine envelope (ERROR log row + notification dispatch).
        # Every content path (_get_screenshots, _get_csv_data,
        # _get_embedded_data, _get_notification_content) funnels through this
        # method, so this is the single choke point.
        if chart is None and dashboard is None:
            if self._report_schedule.chart_id is not None:
                raise ReportScheduleTargetChartDeletedError()
            # Symmetric guard for dashboard targets. Dashboard soft delete lands
            # in the sibling rollout; until then this cannot fire (a dashboard
            # with dependent reports cannot be deleted), which makes it inert
            # rather than wrong — and it keeps the report-target error vocabulary
            # parallel across entities from day one.
            if self._report_schedule.dashboard_id is not None:
                raise ReportScheduleTargetDashboardDeletedError()
            # Defensive fallback for a malformed report with no target IDs.
            # Missing relationships with a target ID are handled by the
            # dedicated deleted-target errors above.
            raise ReportScheduleUnexpectedError(
                f"Report schedule {self._report_schedule.id} "
                f"({self._report_schedule.name!r}) has no resolvable target "
                f"(chart_id={self._report_schedule.chart_id}, "
                f"dashboard_id={self._report_schedule.dashboard_id}); "
                "the report has neither a chart nor a dashboard."
            )

        force = "true" if self._report_schedule.force_screenshot else "false"
        if chart:
            if result_format in {
                ChartDataResultFormat.CSV,
                ChartDataResultFormat.XLSX,
                ChartDataResultFormat.JSON,
                ChartDataResultFormat.XLSX,
            }:
                return get_url_path(
                    "ChartDataRestApi.get_data",
                    pk=self._report_schedule.chart_id,

View on GitHub (pinned to f4587218dd)

Solutions

  1. Inspect the row named in the message: SELECT * FROM report_schedule WHERE id = <id>
  2. Fix the row by setting a valid chart_id or dashboard_id, or delete the malformed schedule
  3. If a custom tool created this row, fix the tool to always set exactly one target

Example fix

-- before: malformed row
UPDATE report_schedule SET chart_id = NULL WHERE id = 42;  -- later execution fails

-- after
UPDATE report_schedule SET chart_id = 123 WHERE id = 42;  -- valid target
Defensive patterns

Strategy: validation

Validate before calling

def report_has_target(report) -> bool:
    return report.chart_id is not None or report.dashboard_id is not None

Type guard

def is_well_formed_report(report_schedule) -> bool:
    """A report must carry exactly one non-null target id."""
    return (report_schedule.chart_id is not None) ^ (report_schedule.dashboard_id is not None)

Try / catch

try:
    _get_url()
except ReportScheduleUnexpectedError as ex:
    if 'no resolvable target' in str(ex):
        # quarantine the malformed row named in the message
        flag_for_repair(report_schedule.id)

Prevention

When it happens

Trigger: A report_schedule row manually inserted (or corrupted) with chart_id and dashboard_id both NULL; bugs in import/export flows that strip target ids; direct DB edits. Normal API paths cannot create a target-less report.

Common situations: Migrations or manual SQL backfills that leave nulls; a partially applied update that cleared the target column; test fixtures created without targets.

Related errors


AI-assisted analysis of apache/superset@f4587218dd (2026-08-14). Data as JSON: /api/errors/b2f84fddf2bb9ada. Report an issue: GitHub.