apache/superset · warning · DashboardNotFoundError

Dashboard not found.

Error message

Dashboard not found.

What it means

DashboardNotFoundError is raised by DeleteDashboardCommand.validate() when DashboardDAO.find_by_ids() returns no rows or fewer rows than the requested id list. At least one dashboard id in the bulk delete request does not exist (never existed, was hard-deleted, or was deleted concurrently).

Source

Thrown at superset/commands/dashboard/delete.py:72

            raise DashboardForbiddenError() from ex


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

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

    def validate(self) -> None:
        # Validate/populate model exists
        self._models = DashboardDAO.find_by_ids(self._model_ids)
        if not self._models or len(self._models) != len(self._model_ids):
            raise DashboardNotFoundError()
        # Check there are no associated ReportSchedules
        if reports := ReportScheduleDAO.find_by_dashboard_ids(self._model_ids):
            report_names = [report.name for report in reports]
            raise DashboardDeleteFailedReportsExistError(
                _(
                    "There are associated alerts or reports: %(report_names)s",
                    report_names=",".join(report_names),
                )
            )
        # Check editorship
        for model in self._models:
            try:
                security_manager.raise_for_editorship(model)
            except SupersetSecurityException as ex:
                raise DashboardForbiddenError() from ex

View on GitHub (pinned to f4587218dd)

Solutions

  1. Re-fetch the dashboard list and retry the delete with only ids that still exist.
  2. Treat 404 on delete as success in idempotent automation (the end state — dashboard absent — is achieved).
  3. Split large bulk deletes into per-id deletes so one missing id does not abort the whole batch.

Example fix

# before
DeleteDashboardCommand([10, 11, 999]).run()  # 999 gone -> whole batch 404s

# after
existing = DashboardDAO.find_by_ids([10, 11, 999])
if existing:
    DeleteDashboardCommand([d.id for d in existing]).run()
Defensive patterns

Strategy: validation

Validate before calling

from superset.daos.dashboard import DashboardDAO

models = DashboardDAO.find_by_ids(ids)
alive_ids = [m.id for m in models]
if len(alive_ids) != len(ids):
    logger.info('skipping missing ids: %s', set(ids) - set(alive_ids))

Try / catch

try:
    DeleteDashboardCommand(ids).run()
except DashboardNotFoundError:
    # treat as already deleted; optionally narrow and retry
    retry_with_existing_ids_only()

Prevention

When it happens

Trigger: DELETE /api/v1/dashboard/ with an id list containing a stale or nonexistent dashboard id; concurrent deletion by another user between listing and deleting; deleting a soft-deleted dashboard whose row the DAO no longer returns.

Common situations: Multi-select bulk delete in the UI after another admin already removed one dashboard; retrying a failed bulk delete where part of the batch succeeded; scripts with cached ids after a metadata DB restore.

Related errors


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