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

  1. 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
  2. Verify the metric id belongs to the given dataset_id (GET /api/v1/dataset/{id}/_metric or fetch the dataset and inspect metrics)
  3. 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

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


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