{"record":{"id":"5e18023565702e1f","repo":"apache/superset","slug":"dataset-existing-table-name-r-uuid-config-uui-5e1802","errorCode":null,"errorMessage":"Dataset {existing.table_name!r} (uuid {config['uuid']}) cannot be restored via re-import because another active dataset already references the same physical table ({incoming_identity.table!r}). Rename one of them and retry.","messagePattern":"Dataset (.+?) \\(uuid (.+?)\\) cannot be restored via re-import because another active dataset already references the same physical table \\((.+?)\\)\\. Rename one of them and retry\\.","errorType":"exception","errorClass":"ImportFailedError","httpStatus":500,"severity":"error","filePath":"superset/commands/dataset/importers/v1/utils.py","lineNumber":298,"sourceCode":"            # already references the same physical table. Otherwise the\n            # restore would produce two live ``SqlaTable`` rows for one\n            # physical table, breaking the logical-uniqueness contract. The\n            # shared DAO helper relies on the SoftDeleteMixin listener to\n            # consider only active rows, excludes ``existing`` itself via\n            # ``id !=``, and normalizes the catalog the same way create/update\n            # uniqueness does.\n            # Probe the POST-update identity: the uploaded config may rename\n            # table_name/schema/catalog, and the collision that matters is\n            # against the identity the restored row will end up with — not\n            # the stale stored one. Absent keys fall back to the stored\n            # values; explicit nulls are respected.\n            incoming_identity = Table(\n                config.get(\"table_name\") or existing.table_name,\n                config.get(\"schema\", existing.schema),\n                config.get(\"catalog\", existing.catalog),\n            )\n            if DatasetDAO.has_active_logical_duplicate(existing, incoming_identity):\n                raise ImportFailedError(\n                    f\"Dataset {existing.table_name!r} (uuid {config['uuid']}) \"\n                    \"cannot be restored via re-import because another active \"\n                    \"dataset already references the same physical table \"\n                    f\"({incoming_identity.table!r}). Rename one of them and \"\n                    \"retry.\"\n                )\n            # Restore in place (clear ``deleted_at``) rather than\n            # hard-delete-and-replace: a hard delete would cascade through the\n            # chart back-reference and the delete-orphan child relationships\n            # (table_columns, sql_metrics), which the import would then need to\n            # reconstruct.\n            #\n            # How the restore lands as an UPDATE: clearing\n            # ``existing.deleted_at`` marks the in-session row dirty and the\n            # explicit flush emits the ``deleted_at = NULL`` UPDATE before\n            # ``SqlaTable.import_from_dict`` (below) does its own query-by-uuid\n            # lookup. Without the flush we would be relying on autoflush ahead\n            # of that internal query — correct under default session config but","sourceCodeStart":280,"sourceCodeEnd":316,"githubUrl":"https://github.com/apache/superset/blob/f4587218dd19d046c3e4d00063e7d27f8a2ed354/superset/commands/dataset/importers/v1/utils.py#L280-L316","documentation":"Before clearing deleted_at on restore, the import checks DatasetDAO.has_active_logical_duplicate against the post-update physical identity (table_name/schema/catalog from the uploaded config, falling back to stored values). If another active dataset row already references the same physical table, the restore is refused so there are never two live SqlaTable rows for one physical table.","triggerScenarios":"Importing a bundle to restore a soft-deleted dataset while another dataset (created after the deletion) already occupies the same database+schema+table (or catalog) identity — including when the upload renames the dataset onto an occupied identity.","commonSituations":"Dataset deleted, a new one created with the same table, then an old export is re-imported; uploads that change table_name/schema in the YAML colliding with an existing live dataset.","solutions":["Rename the table_name/schema/catalog in the uploaded YAML to an unoccupied identity and retry","Rename or soft-delete the other active dataset that now owns the physical table, then retry the import","Restore the newer dataset instead of re-importing the old one"],"exampleFix":"# before (YAML)\ntable_name: sales\nschema: public\n# after (avoid the collision)\ntable_name: sales_restored\nschema: public","handlingStrategy":"validation","validationCode":"from superset.daos.dataset import DatasetDAO\nfrom superset.models.helpers import Table  # identity namedtuple pattern\n\nincoming = Table(config.get('table_name'), config.get('schema'), config.get('catalog'))\nexisting = find_existing_for_import(SqlaTable, config['uuid'])\nif existing is not None and existing.deleted_at is not None:\n    if DatasetDAO.has_active_logical_duplicate(existing, incoming):\n        raise ValueError(f'identity {incoming} already taken by an active dataset — rename first')","typeGuard":null,"tryCatchPattern":"from superset.commands.exceptions import ImportFailedError\ntry:\n    import_dataset(config)\nexcept ImportFailedError as ex:\n    if 'another active dataset already references the same physical table' in str(ex):\n        config['table_name'] += '_imported'  # disambiguate and retry once\n        import_dataset(config)","preventionTips":["Avoid deleting-then-recreating datasets with the same physical identity","When renaming datasets, check the target identity is free (including trash)","Adopt a naming convention for restored datasets"],"tags":["dataset-import","uniqueness","soft-delete","conflict","superset"],"backgroundTag":null,"analyzedSha":"f4587218dd19d046c3e4d00063e7d27f8a2ed354","analyzedAt":"2026-08-14T22:39:27.425Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}