apache/superset · error · ReportScheduleNotFoundError

Report Schedule not found.

Error message

Report Schedule not found.

What it means

ReportScheduleNotFoundError from DeleteReportCommand.validate: the command resolves every id in _model_ids via ReportScheduleDAO.find_by_ids and requires the result count to equal the request count. Any missing, already-deleted, or access-filtered id makes the lists mismatch and the delete aborts.

Source

Thrown at superset/commands/report/delete.py:51

logger = logging.getLogger(__name__)


class DeleteReportScheduleCommand(BaseCommand):
    def __init__(self, model_ids: list[int]):
        self._model_ids = model_ids
        self._models: Optional[list[ReportSchedule]] = None

    @transaction(on_error=partial(on_error, reraise=ReportScheduleDeleteFailedError))
    def run(self) -> None:
        self.validate()
        assert self._models
        ReportScheduleDAO.delete(self._models)

    def validate(self) -> None:
        # Validate/populate model exists
        self._models = ReportScheduleDAO.find_by_ids(self._model_ids)
        if not self._models or len(self._models) != len(self._model_ids):
            raise ReportScheduleNotFoundError()

        # Check editorship
        for model in self._models:
            try:
                security_manager.raise_for_editorship(model)
            except SupersetSecurityException as ex:
                raise ReportScheduleForbiddenError() from ex

View on GitHub (pinned to f4587218dd)

Solutions

  1. GET /api/v1/report/{id} first to confirm existence, or filter your id list against current rows
  2. Treat 404 as success in idempotent cleanup scripts (the end state — report absent — is achieved)
  3. Guard the UI against double-delete submissions

Example fix

# before
if not sm.get_by_id(report_id):
    pass  # fall through and still try to delete
DeleteReportCommand(user, [report_id]).run()

# after
if ReportScheduleDAO.find_by_id(report_id) is None:
    return  # already gone; nothing to do
DeleteReportCommand(user, [report_id]).run()
Defensive patterns

Strategy: type-guard

Validate before calling

from superset.reports.dao import ReportScheduleDAO

ids = [i for i in requested_ids if ReportScheduleDAO.find_by_id(i) is not None]
if not ids:
    return  # nothing deletable; already gone

Type guard

def deletable_ids(ids: list[int]) -> list[int]:
    found = {m.id for m in ReportScheduleDAO.find_by_ids(ids) or []}
    return [i for i in ids if i in found]

Try / catch

try:
    DeleteReportCommand(user, ids).run()
except ReportScheduleNotFoundError:
    pass  # treat as success in idempotent cleanup

Prevention

When it happens

Trigger: DELETE /api/v1/report/ with a list where at least one id does not exist (deleted moments before, wrong id, or filtered by row-level rules). Also when two concurrent deletes race and one removes the row first.

Common situations: Frontend double-submit of a delete button; cleanup scripts holding stale ids; another admin deleted the report between listing and deleting.

Related errors


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