{"record":{"id":"2620b8987132be13","repo":"apache/superset","slug":"changing-this-report-is-forbidden-2620b8","errorCode":null,"errorMessage":"Changing this report is forbidden","messagePattern":"Changing this report is forbidden","errorType":"exception","errorClass":"ReportScheduleForbiddenError","httpStatus":403,"severity":"error","filePath":"superset/commands/report/delete.py","lineNumber":58,"sourceCode":"\n    @transaction(on_error=partial(on_error, reraise=ReportScheduleDeleteFailedError))\n    def run(self) -> None:\n        self.validate()\n        assert self._models\n        ReportScheduleDAO.delete(self._models)\n\n    def validate(self) -> None:\n        # Validate/populate model exists\n        self._models = ReportScheduleDAO.find_by_ids(self._model_ids)\n        if not self._models or len(self._models) != len(self._model_ids):\n            raise ReportScheduleNotFoundError()\n\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 ReportScheduleForbiddenError() from ex\n","sourceCodeStart":40,"sourceCodeEnd":59,"githubUrl":"https://github.com/apache/superset/blob/f4587218dd19d046c3e4d00063e7d27f8a2ed354/superset/commands/report/delete.py#L40-L59","documentation":"ReportScheduleForbiddenError from DeleteReportCommand.validate: for each loaded model the command calls security_manager.raise_for_editorship(model), and a raised SupersetSecurityException is wrapped as 'Changing this report is forbidden'. Only owners (or admins) may delete a report schedule.","triggerScenarios":"DELETE /api/v1/report/{id} by a user who is not in the report's owners list and lacks admin rights. Mixed batches where the caller owns some but not all targeted reports also fail on the first non-owned model.","commonSituations":"A teammate left and their reports are orphaned; scripts run as a service account that never owned the schedules; role has can_delete on ReportSchedule but the object-level owner check still denies.","solutions":["Have an admin (or the current owner) perform the delete, or add the caller to the report's owners first","For service-account automation, create reports with that account as owner from the start","Batch deletes should be pre-filtered to rows the caller owns"],"exampleFix":"# before\nDeleteReportCommand(current_user, [report_id]).run()  # not an owner -> 403\n\n# after\n# admin adds the caller as owner, then:\nUpdateReportCommand(admin, report_id, {'owners': [*old_owners, current_user.id]}).run()\nDeleteReportCommand(current_user, [report_id]).run()","handlingStrategy":"validation","validationCode":"from superset import security_manager\n\nfor rid in report_ids:\n    model = ReportScheduleDAO.find_by_id(rid)\n    if model and not security_manager.is_owner(model):\n        raise PermissionError(f'not an owner of report {rid}')  # fail before the command","typeGuard":null,"tryCatchPattern":"try:\n    DeleteReportCommand(user, ids).run()\nexcept ReportScheduleForbiddenError:\n    # route to an admin, or add caller to owners first\n    ...","preventionTips":["Create reports with the service account that will manage them as an owner","Batch deletes should be pre-filtered to owned rows","When owners leave, transfer ownership before deactivating their accounts"],"tags":["alerts-reports","authorization","rbac","delete"],"backgroundTag":null,"analyzedSha":"f4587218dd19d046c3e4d00063e7d27f8a2ed354","analyzedAt":"2026-08-14T22:39:27.425Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}