apache/superset · error · ImportFailedError

Chart {existing.slice_name!r} (uuid {config['uuid']}) was de

Error message

Chart {existing.slice_name!r} (uuid {config['uuid']}) was deleted and re-import requires can_write permission to restore it

What it means

Raised as ImportFailedError on the RESTORE path of chart import: the bundle's UUID matches a chart row that is soft-deleted, and the importing caller lacks can_write permission, so Superset refuses to implicitly restore the deleted chart. The error names the chart and UUID so operators can identify which item in a multi-chart bundle failed.

Source

Thrown at superset/commands/chart/importers/v1/utils.py:77

def _restore_existing_chart_for_import(
    existing: Slice,
    config: dict[str, Any],
    can_write: bool,
    user: Any,
) -> 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 the dashboard
        # importer reattach to a deleted chart and produce a broken
        # dashboard.
        # Name the chart: a dashboard bundle imports many charts, and
        # without the identity the operator can't tell which of N
        # charts in the bundle hit the soft-deleted match.
        raise ImportFailedError(
            f"Chart {existing.slice_name!r} (uuid {config['uuid']}) "
            f"was deleted and re-import requires can_write "
            f"permission to restore it"
        )
    # ``user`` is None on background / example-loader paths; combined
    # with ``can_write`` (typically from ``ignore_permissions=True``)
    # the editorship check is intentionally skipped because the caller
    # already established trust.
    _ensure_can_edit_existing_chart(
        existing,
        user,
        f"Chart {existing.slice_name!r} (uuid {config['uuid']}) "
        f"already exists and user doesn't have permissions to restore it",
    )
    # Restore in place (clear ``deleted_at``) rather than
    # hard-delete-and-replace: a hard delete would cascade to
    # dashboard_slices and other FK references, breaking the dashboards
    # that previously embedded this chart.

View on GitHub (pinned to f4587218dd)

Solutions

  1. Run the import as a user/role with can_write permission (typically Admin) so the soft-deleted chart is restored and updated.
  2. Or purge the soft-deleted row first, then import as a plain create.
  3. Or change the bundle's UUID and import as a brand-new chart.

Example fix

# before: restricted user
client.post('/api/v1/chart/import/', files=...)  # ImportFailedError: ...was deleted and re-import requires can_write

# after: import with an account holding can_write, or hard-delete the tombstone row first
Slice.query.filter_by(uuid=uuid.UUID(bundle_uuid), deleted_at.isnot(None)).delete()
Defensive patterns

Strategy: validation

Validate before calling

existing = find_existing_for_import(Slice, config['uuid'])
if existing is not None and existing.deleted_at is not None and not can_write:
    raise PermissionError('soft-deleted chart requires can_write to restore')

Type guard

def is_soft_deleted(chart) -> bool:
    return chart is not None and chart.deleted_at is not None

Try / catch

from superset.commands.importers.exceptions import ImportFailedError
try:
    import_chart(...)
except ImportFailedError as ex:
    if 'requires can_write' in str(ex):
        escalate_to_admin_import()

Prevention

When it happens

Trigger: Re-importing an export whose chart was deleted in the target workspace, by a user/role without can_write (e.g. ignore_permissions not set, non-privileged importer).

Common situations: Restoring a deleted chart from a backup bundle without admin rights; example-loader or background imports running with a restricted principal.

Related errors


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