apache/superset · error · AmbiguousPurgeTargetError

uuid={self._uuid!r} matches {', '.join(sorted(type(m).__name

Error message

uuid={self._uuid!r} matches {', '.join(sorted(type(m).__name__ for m in matches))}; pass the entity type to disambiguate

What it means

AmbiguousPurgeTargetError is raised by the force-purge command's _resolve() when a single uuid matches more than one soft-delete entity type (it queries every candidate model with skip_visibility_filter and deleted_at filters). The message lists the matched type names and instructs you to pass the entity type to disambiguate, since Superset uuids are only unique per model class, not globally.

Source

Thrown at superset/commands/deletion_retention/force_purge.py:109

        """
        candidates = (
            [self._model_cls]
            if self._model_cls is not None
            else SoftDeleteMixin._registered_subclasses  # noqa: SLF001
        )
        matches: list[SoftDeleteMixin] = []
        for model in candidates:
            if not hasattr(model, "uuid"):
                continue
            with skip_visibility_filter(db.session, model):
                query = db.session.query(model).filter(model.uuid == self._uuid)
                if self._require_archived:
                    query = query.filter(model.deleted_at.is_not(None))
                entity = query.first()
            if entity is not None:
                matches.append(entity)
        if len(matches) > 1:
            raise AmbiguousPurgeTargetError(
                f"uuid={self._uuid!r} matches "
                f"{', '.join(sorted(type(m).__name__ for m in matches))}; "
                "pass the entity type to disambiguate"
            )
        return matches[0] if matches else None

    def run(self) -> dict[str, Any]:
        """Resolve + purge. Returns a summary; a no-op when nothing matches."""
        audit.reconcile_pending()
        entity = self._resolve()
        if entity is None:
            logger.info("force_purge: no entity for uuid=%s (no-op)", self._uuid)
            return {"purged": False, "reason": "not_found", "uuid": self._uuid}

        entity_type = str(cast(Any, type(entity)).__tablename__)
        removed_dashboard_slices = dashboard_slice_count(db.session, entity)
        # The audit row commits independently. Release the resolving read
        # transaction first, then resolve again against post-audit state.

View on GitHub (pinned to f4587218dd)

Solutions

  1. Pass the entity type (e.g. 'dashboard' / 'chart') to the force-purge call so _resolve only queries that model — exactly what the message suggests.
  2. If unsure which entity is intended, inspect each matched object first (query by uuid per model) and pick by content.
  3. Fix the underlying data: re-uuid the wrongly duplicated entity so future purges are unambiguous.
  4. Record entity kind alongside uuid in your automation's delete queues.

Example fix

# before
ForcePurgeCommand(uuid="abc-...").run()
# raises: uuid=... matches Chart, Dashboard; pass the entity type to disambiguate

# after
ForcePurgeCommand(uuid="abc-...", entity_type="dashboard").run()
Defensive patterns

Strategy: validation

Validate before calling

matches = {type(m).__name__ for m in query_all_models_by_uuid(u) if m}
if len(matches) > 1:
    require_entity_type_param()  # refuse to purge ambiguously

Try / catch

try:
    ForcePurgeCommand(uuid=u).run()
except AmbiguousPurgeTargetError as ex:
    kind = ask_user_or_policy(str(ex))  # message lists candidate types
    ForcePurgeCommand(uuid=u, entity_type=kind).run()

Prevention

When it happens

Trigger: Invoking the force-purge flow by uuid alone when the same uuid exists on e.g. both a Dashboard and a Chart (copied/duplicated objects can share uuids across types after export/import or manual writes). With _require_archived, matches are restricted to soft-deleted rows, but two different archived models can still collide.

Common situations: Export/import of dashboards that re-created objects with existing uuids. Duplicate-then-delete workflows that copy a uuid onto another entity type. Automated cleanup scripts that purge by uuid harvested from audit logs without recording the entity kind.

Related errors


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