apache/superset · error · DatasetMetricNotFoundError
Dataset metric not found.
Error message
Dataset metric not found.
What it means
DatasetMetricDeleteCommand.validate() loads the metric via DatasetDAO.find_dataset_metric(dataset_id, model_id); when no SqlMetric with that id exists under that dataset, DatasetMetricNotFoundError is raised (and on_error maps failures to DatasetMetricDeleteFailedError). It means the (dataset_id, metric_id) pair does not resolve — deleted already, wrong dataset, or wrong id.
Source
Thrown at superset/commands/dataset/metrics/delete.py:52
class DeleteDatasetMetricCommand(BaseCommand):
def __init__(self, dataset_id: int, model_id: int):
self._dataset_id = dataset_id
self._model_id = model_id
self._model: Optional[SqlMetric] = None
@transaction(on_error=partial(on_error, reraise=DatasetMetricDeleteFailedError))
def run(self) -> None:
self.validate()
assert self._model
DatasetMetricDAO.delete([self._model])
def validate(self) -> None:
# Validate/populate model exists
self._model = DatasetDAO.find_dataset_metric(self._dataset_id, self._model_id)
if not self._model:
raise DatasetMetricNotFoundError()
# Check editorship
try:
security_manager.raise_for_editorship(self._model)
except SupersetSecurityException as ex:
raise DatasetMetricForbiddenError() from ex
View on GitHub (pinned to f4587218dd)
Solutions
- Refresh the dataset in the UI and re-check the metric list; the metric is most likely already gone — treat 404 as success in idempotent flows
- Verify the metric id belongs to the given dataset_id (GET /api/v1/dataset/{id}/_metric or fetch the dataset and inspect metrics)
- Fix the caller to pass the correct dataset/metric pair
Example fix
# before
metric = dataset.metrics[0]
client.delete(f"/api/v1/dataset/{dataset_id}/metric/{metric.id}")
# after — tolerate already-deleted metric (idempotent delete)
resp = client.delete(f"/api/v1/dataset/{dataset_id}/metric/{metric.id}")
if resp.status_code == 404:
pass # already deleted Defensive patterns
Strategy: validation
Validate before calling
from superset.daos.dataset import DatasetDAO
metric = DatasetDAO.find_dataset_metric(dataset_id, metric_id)
if metric is None:
# treat as already deleted — skip instead of calling DELETE
... Type guard
def metric_belongs_to_dataset(metrics, metric_id, dataset_id) -> bool:
return any(m.id == metric_id for m in metrics) and dataset_id is not None Try / catch
from superset.commands.dataset.metrics.exceptions import (
DatasetMetricNotFoundError, DatasetMetricDeleteFailedError,
)
try:
DatasetMetricDeleteCommand(dataset_id, metric_id).run()
except (DatasetMetricNotFoundError, DatasetMetricDeleteFailedError) as ex:
if 'not found' in str(ex).lower():
pass # idempotent: already deleted by someone else
else:
raise Prevention
- Make delete flows idempotent: treat 404/not-found as success
- Refresh the metric list before showing delete buttons in custom UIs
- Always address metrics as (dataset_id, metric_id) pairs fetched from the live dataset
When it happens
Trigger: DELETE /api/v1/dataset/{dataset_id}/metric/{metric_id} where the metric id does not belong to that dataset, was already deleted, or the dataset id is wrong.
Common situations: Stale UI after another user/session deleted the metric; race between two clients editing the same dataset; copy-pasted or hardcoded ids that drifted.
Related errors
- Error in jinja expression in metric expression: %(msg)s
- Chart not found.
- CSS template not found.
- Dashboard not found.
- Database not found.
AI-assisted analysis of apache/superset@f4587218dd (2026-08-14).
Data as JSON: /api/errors/2542bce5bf3ecff2.
Report an issue: GitHub.