apache/superset · error · ImportFailedError

Dashboard was deleted and re-import requires can_write permi

Error message

Dashboard was deleted and re-import requires can_write permission to restore it

What it means

ImportFailedError('Dashboard was deleted and re-import requires can_write permission to restore it') is raised in the v1 import path when the bundle's dashboard UUID matches a soft-deleted dashboard row (deleted_at set) and the importer lacks can_write (import permission). Re-importing a soft-deleted UUID is treated as an implicit restore, which is a write-level operation, so it is refused rather than silently reviving the row.

Source

Thrown at superset/commands/dashboard/importers/v1/utils.py:337

        "Dashboard",
    )
    # `user` is None for background / example-loader paths (no Flask request
    # user). Combined with ``can_write=True`` (typically from
    # ``ignore_permissions=True``), the editorship checks in the restore /
    # overwrite branches below are intentionally skipped because the caller has
    # already established trust at the command level.
    user = get_user()
    if existing := find_existing_for_import(Dashboard, config["uuid"]):
        if existing.deleted_at is not None:
            # RESTORE path — re-importing a soft-deleted UUID is an implicit
            # restore-with-update, a distinct operation from overwriting an
            # alive row, so it is handled in its own branch.
            if not can_write:
                # Case B: don't silently return a soft-deleted row to a caller
                # without write permission — that would let a dependent import
                # (e.g. a dashboard zip referencing this dashboard) reattach to
                # a deleted dashboard.
                raise ImportFailedError(
                    "Dashboard was deleted and re-import requires can_write "
                    "permission to restore it"
                )
            # ``user`` is None on background / example-loader paths (no Flask
            # request user); combined with ``can_write`` (typically from
            # ``ignore_permissions=True``) the editorship check is intentionally
            # skipped because the caller already established trust.
            if user and (
                not security_manager.can_access_dashboard(existing)
                or (
                    not security_manager.is_editor(existing)
                    and not security_manager.is_admin()
                )
            ):
                raise ImportFailedError(
                    "A dashboard already exists and user doesn't have "
                    "permissions to restore it"
                )

View on GitHub (pinned to f4587218dd)

Solutions

  1. Run the import with a user/role that has dashboard write permission (can_write / can_import on Dashboard).
  2. Or purge the soft-deleted dashboard first (hard-delete via the API's purge path or admin) so the UUID is free and the import becomes a plain create — still requiring create permission.
  3. If the dashboard should stay deleted, remove it from the bundle or change its UUID before import.

Example fix

# before
client.post('/api/v1/dashboard/import/', ...)  # viewer token, UUID soft-deleted

# after
# 1) purge the soft-deleted row as admin, or
# 2) re-run import with a role that has can_write on Dashboard
admin_client.post('/api/v1/dashboard/import/', files=...)
Defensive patterns

Strategy: try-catch

Validate before calling

from superset.commands.dashboard.importers.v1.utils import find_existing_for_import
from superset.models.dashboard import Dashboard

existing = find_existing_for_import(Dashboard, config['uuid'])
if existing is not None and existing.deleted_at is not None:
    if not security_manager.can_access('can_write', 'Dashboard'):
        raise PermissionError('re-import of soft-deleted UUID needs can_write')

Try / catch

try:
    run_import(bundle)
except ImportFailedError as ex:
    if 'requires can_write permission to restore' in str(ex):
        escalate_to_writer_or_purge_soft_deleted_row()

Prevention

When it happens

Trigger: Importing a v1 dashboard ZIP whose dashboard UUID corresponds to a dashboard that was soft-deleted in the target instance, using an account (or token) without dashboard write/import permission.

Common situations: Re-importing an old export after the dashboard was deleted during cleanup; automated sync pipelines that push bundles with a viewer-level service account; deleted dashboards in the trash-like soft-delete state blocking re-import for non-writers.

Related errors


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