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
- GET /api/v1/report/{id} first to confirm existence, or filter your id list against current rows
- Treat 404 as success in idempotent cleanup scripts (the end state — report absent — is achieved)
- 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
- Make delete flows idempotent: 404 means the desired end state already holds
- Filter id batches through a find_by_ids call before deleting
- Debounce delete buttons to avoid double-submit races
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
- Chart not found.
- Dashboard %(dashboard_id)s not found
- Annotation layer not found.
- There are associated alerts or reports: %(report_names)s
- Chart not found.
AI-assisted analysis of apache/superset@f4587218dd (2026-08-14).
Data as JSON: /api/errors/d27b5d3baae7187a.
Report an issue: GitHub.