{"record":{"id":"2542bce5bf3ecff2","repo":"apache/superset","slug":"dataset-metric-not-found","errorCode":null,"errorMessage":"Dataset metric not found.","messagePattern":"Dataset metric not found\\.","errorType":"exception","errorClass":"DatasetMetricNotFoundError","httpStatus":500,"severity":"error","filePath":"superset/commands/dataset/metrics/delete.py","lineNumber":52,"sourceCode":"\n\nclass DeleteDatasetMetricCommand(BaseCommand):\n    def __init__(self, dataset_id: int, model_id: int):\n        self._dataset_id = dataset_id\n        self._model_id = model_id\n        self._model: Optional[SqlMetric] = None\n\n    @transaction(on_error=partial(on_error, reraise=DatasetMetricDeleteFailedError))\n    def run(self) -> None:\n        self.validate()\n        assert self._model\n        DatasetMetricDAO.delete([self._model])\n\n    def validate(self) -> None:\n        # Validate/populate model exists\n        self._model = DatasetDAO.find_dataset_metric(self._dataset_id, self._model_id)\n        if not self._model:\n            raise DatasetMetricNotFoundError()\n        # Check editorship\n        try:\n            security_manager.raise_for_editorship(self._model)\n        except SupersetSecurityException as ex:\n            raise DatasetMetricForbiddenError() from ex\n","sourceCodeStart":34,"sourceCodeEnd":58,"githubUrl":"https://github.com/apache/superset/blob/f4587218dd19d046c3e4d00063e7d27f8a2ed354/superset/commands/dataset/metrics/delete.py#L34-L58","documentation":"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.","triggerScenarios":"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.","commonSituations":"Stale UI after another user/session deleted the metric; race between two clients editing the same dataset; copy-pasted or hardcoded ids that drifted.","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"],"exampleFix":"# before\nmetric = dataset.metrics[0]\nclient.delete(f\"/api/v1/dataset/{dataset_id}/metric/{metric.id}\")\n# after — tolerate already-deleted metric (idempotent delete)\nresp = client.delete(f\"/api/v1/dataset/{dataset_id}/metric/{metric.id}\")\nif resp.status_code == 404:\n    pass  # already deleted","handlingStrategy":"validation","validationCode":"from superset.daos.dataset import DatasetDAO\n\nmetric = DatasetDAO.find_dataset_metric(dataset_id, metric_id)\nif metric is None:\n    # treat as already deleted — skip instead of calling DELETE\n    ...","typeGuard":"def metric_belongs_to_dataset(metrics, metric_id, dataset_id) -> bool:\n    return any(m.id == metric_id for m in metrics) and dataset_id is not None","tryCatchPattern":"from superset.commands.dataset.metrics.exceptions import (\n    DatasetMetricNotFoundError, DatasetMetricDeleteFailedError,\n)\ntry:\n    DatasetMetricDeleteCommand(dataset_id, metric_id).run()\nexcept (DatasetMetricNotFoundError, DatasetMetricDeleteFailedError) as ex:\n    if 'not found' in str(ex).lower():\n        pass  # idempotent: already deleted by someone else\n    else:\n        raise","preventionTips":["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"],"tags":["dataset","metric","delete","not-found","superset"],"backgroundTag":null,"analyzedSha":"f4587218dd19d046c3e4d00063e7d27f8a2ed354","analyzedAt":"2026-08-14T22:39:27.425Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}