apache/superset · error · DashboardForbiddenError

Changing this Dashboard is forbidden

Error message

Changing this Dashboard is forbidden

What it means

DashboardForbiddenError is raised by DeleteEmbeddedDashboardCommand.validate() when security_manager.raise_for_editorship(dashboard) throws SupersetSecurityException. Deleting a dashboard's embedded configuration counts as modifying the dashboard, so only owners/editors/admins may do it.

Source

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

from superset.utils.decorators import on_error, transaction

logger = logging.getLogger(__name__)


class DeleteEmbeddedDashboardCommand(BaseCommand):
    def __init__(self, dashboard: Dashboard):
        self._dashboard = dashboard

    @transaction(on_error=partial(on_error, reraise=DashboardDeleteEmbeddedFailedError))
    def run(self) -> None:
        self.validate()
        return EmbeddedDashboardDAO.delete(self._dashboard.embedded)

    def validate(self) -> None:
        try:
            security_manager.raise_for_editorship(self._dashboard)
        except SupersetSecurityException as ex:
            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()

View on GitHub (pinned to f4587218dd)

Solutions

  1. Perform the embedded deletion as a dashboard owner or admin user.
  2. For service accounts, grant a role with dashboard edit/editor permission on that dashboard.
  3. Check the actor in background jobs: embedded deletion in async code inherits the job's user context, which may be None or a limited user.

Example fix

# before
DeleteEmbeddedDashboardCommand(dash).run()  # viewer user -> 403

# after
from superset.extensions import security_manager
try:
    DeleteEmbeddedDashboardCommand(dash).run()
except DashboardForbiddenError:
    # re-run as owner or surface a permission prompt
    raise
Defensive patterns

Strategy: try-catch

Validate before calling

from superset.extensions import security_manager

try:
    security_manager.raise_for_editorship(dashboard)
except SupersetSecurityException:
    raise PermissionError('editorship required to delete embedded config')

Try / catch

try:
    DeleteEmbeddedDashboardCommand(dashboard).run()
except DashboardForbiddenError:
    # retry as owner/admin or surface permission error to user
    escalate_permission_error(dashboard.id)

Prevention

When it happens

Trigger: Calling the embedded-delete flow (DELETE on the dashboard's embedded resource, e.g. /api/v1/dashboard/<id>/embedded) as a user who cannot edit the dashboard. Also triggered by code paths that clear embedded config during dashboard updates when the actor is a viewer.

Common situations: Non-owner admins of restricted deployments trying to disable embedded mode; automated cleanup scripts running under a low-privilege service account; embedded-guest flows that mistakenly invoke deletion.

Understand the failure class

Related errors


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