{"record":{"id":"c6d58ae198d4da76","repo":"apache/superset","slug":"changing-this-dataset-is-forbidden-c6d58a","errorCode":null,"errorMessage":"Changing this dataset is forbidden","messagePattern":"Changing this dataset is forbidden","errorType":"exception","errorClass":"DatasetForbiddenError","httpStatus":403,"severity":"error","filePath":"superset/commands/dataset/delete.py","lineNumber":57,"sourceCode":"        self._models: Optional[list[SqlaTable]] = None\n\n    @transaction(on_error=partial(on_error, reraise=DatasetDeleteFailedError))\n    def run(self) -> None:\n        self.validate()\n        assert self._models\n        DatasetDAO.delete(self._models)\n\n    def validate(self) -> None:\n        # Validate/populate model exists\n        self._models = DatasetDAO.find_by_ids(self._model_ids)\n        if not self._models or len(self._models) != len(self._model_ids):\n            raise DatasetNotFoundError()\n        # Check editorship\n        for model in self._models:\n            try:\n                security_manager.raise_for_editorship(model)\n            except SupersetSecurityException as ex:\n                raise DatasetForbiddenError() from ex\n","sourceCodeStart":39,"sourceCodeEnd":58,"githubUrl":"https://github.com/apache/superset/blob/f4587218dd19d046c3e4d00063e7d27f8a2ed354/superset/commands/dataset/delete.py#L39-L58","documentation":"DatasetForbiddenError (HTTP 403, 'Changing this dataset is forbidden') is raised by the dataset delete command when security_manager.raise_for_editorship(model) throws for any model in the bulk set. Every dataset in the delete payload must be editable by the caller; a single non-editable dataset blocks the entire bulk delete.","triggerScenarios":"DELETE /api/v1/dataset/ with multiple ids where the caller owns some but not all; a user with dataset read access attempting deletion; ownership of one dataset in the selection belonging to another user.","commonSituations":"Bulk cleanup across teams where a mixed set of owned/unowned datasets is selected; service accounts with broad read grants but no editorship; Gamma users deleting shared datasets.","solutions":["Split the request: delete only datasets where the caller is owner/editor; leave the rest to their owners (or an Admin).","Check owners/editors per dataset via GET /api/v1/dataset/?q=(id:...) before building the bulk payload.","If the operation is legitimately centralized, perform it as Admin or grant the caller ownership of the affected datasets first."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"# Partition the bulk set by editorship before deleting\nfrom superset import security_manager\nfrom superset.daos.dataset import DatasetDAO\n\ndef split_by_editorship(ids: list[int]) -> tuple[list[int], list[int]]:\n    editable, forbidden = [], []\n    for model in DatasetDAO.find_by_ids(ids) or []:\n        try:\n            security_manager.raise_for_editorship(model)\n            editable.append(model.id)\n        except Exception:\n            forbidden.append(model.id)\n    return editable, forbidden","typeGuard":null,"tryCatchPattern":"from superset.commands.dataset.exceptions import DatasetForbiddenError\neditable, forbidden = split_by_editorship(ids)\ntry:\n    DeleteDatasetsCommand(editable).run()\nexcept DatasetForbiddenError:\n    # ownership changed mid-flight: re-partition and retry the remainder once\n    editable2, forbidden2 = split_by_editorship(editable)\n    if editable2:\n        DeleteDatasetsCommand(editable2).run()","preventionTips":["Bulk-delete only what the caller owns/edits; route the rest to owners or Admin.","Re-check editorship right before submit when selections sit in the UI for a while.","Give cleanup automation an Admin context or explicit ownership grants instead of retrying 403s."],"tags":["dataset","authorization","rbac","bulk-delete","flask-api"],"backgroundTag":null,"analyzedSha":"f4587218dd19d046c3e4d00063e7d27f8a2ed354","analyzedAt":"2026-08-14T22:39:27.425Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}