apache/superset · error · DatabaseDeleteSoftDeletedDatasetsExistFailedError
Cannot delete a database whose only remaining datasets are s
Error message
Cannot delete a database whose only remaining datasets are soft-deleted. Restore them (POST /api/v1/dataset/<uuid>/restore) and delete them permanently once a purge capability ships, or remove the underlying rows out-of-band, before deleting the database.
What it means
DatabaseDeleteSoftDeletedDatasetsExistFailedError raised in DeleteDatabaseCommand.validate (delete.py:102) when has_live is false but has_any is true: every remaining SqlaTable row referencing the database is soft-deleted, yet those rows still physically FK-reference the database (tables.database_id), so a hard delete would violate referential integrity. The long message tells the operator exactly that: restore the datasets via POST /api/v1/dataset/<uuid>/restore and delete them permanently, or remove the rows out-of-band, until a purge capability ships.
Source
Thrown at superset/commands/database/delete.py:102
db.session.query(SqlaTable.id)
.filter(
SqlaTable.database_id == self._model_id,
SqlaTable.deleted_at.is_(None),
)
.exists()
).scalar()
has_any = db.session.query(
db.session.query(SqlaTable.id)
.filter(SqlaTable.database_id == self._model_id)
.exists()
).scalar()
# Both cases block the delete (a soft-deleted dataset still FK-references
# the database), but the message differs: with only hidden rows left the
# operator's dataset list looks empty, so say so explicitly.
if has_live:
raise DatabaseDeleteDatasetsExistFailedError()
if has_any:
raise DatabaseDeleteSoftDeletedDatasetsExistFailedError()
View on GitHub (pinned to f4587218dd)
Solutions
- For each soft-deleted dataset: POST /api/v1/dataset/<uuid>/restore, then DELETE /api/v1/dataset/<pk> (hard delete) — repeat until none remain.
- If the REST purge path is insufficient for bulk cleanup, remove the rows out-of-band with a targeted metadata-DB script (DELETE FROM tables WHERE database_id = <id> AND deleted_at IS NOT NULL), with a backup first.
- Retry DELETE /api/v1/database/<id> once both counts (live and any) are zero.
- Track upstream: once a dataset purge capability ships, prefer it over manual SQL.
Example fix
# before: delete fails — only soft-deleted datasets remain DELETE /api/v1/database/5 # -> DatabaseDeleteSoftDeletedDatasetsExistFailedError # after: purge each soft-deleted dataset, then delete the DB POST /api/v1/dataset/<uuid>/restore DELETE /api/v1/dataset/<pk> DELETE /api/v1/database/5
Defensive patterns
Strategy: validation
Validate before calling
from superset.connectors.sqla.models import SqlaTable
from superset import db
def soft_deleted_dataset_ids(database_id: int) -> list:
return [
row[0]
for row in db.session.query(SqlaTable.id, SqlaTable.uuid)
.filter(SqlaTable.database_id == database_id)
.all()
] # if the live count is 0 but this is non-empty, the delete will fail Try / catch
from superset.commands.database.exceptions import (
DatabaseDeleteSoftDeletedDatasetsExistFailedError,
)
try:
DeleteDatabaseCommand(model_id).run()
except DatabaseDeleteSoftDeletedDatasetsExistFailedError:
for uuid in soft_deleted_uuids(model_id):
requests.post(f"/api/v1/dataset/{uuid}/restore")
requests.delete(f"/api/v1/dataset/{pk_of(uuid)}")
DeleteDatabaseCommand(model_id).run() # retry Prevention
- UI dataset deletion is soft deletion — it does NOT free the database for hard delete.
- Purge soft-deleted datasets (restore then delete) as part of database decommissioning runbooks.
- An empty-looking dataset list can still block the delete; trust the error, not the UI.
When it happens
Trigger: DELETE /api/v1/database/<id> after all its datasets were 'deleted' in the UI (which soft-deletes them); the dataset list looks empty so the operator expects the delete to succeed.
Common situations: Teams that cleaned up datasets via the UI before decommissioning a database; environments on dialects where the FK would turn this into an opaque 500 instead.
Related errors
- Cannot delete a database that has datasets attached
- Database not found.
- There are associated alerts or reports: %(report_names)s
- A soft-deleted dataset (uuid %(uuid)s) already references th
- A soft-deleted dataset (uuid %(uuid)s) already references th
AI-assisted analysis of apache/superset@f4587218dd (2026-08-14).
Data as JSON: /api/errors/1c9cde44d1cc2468.
Report an issue: GitHub.