{"record":{"id":"701bc9cd658a4578","repo":"apache/superset","slug":"changing-this-dashboard-is-forbidden-701bc9","errorCode":null,"errorMessage":"Changing this Dashboard is forbidden","messagePattern":"Changing this Dashboard is forbidden","errorType":"http","errorClass":"DashboardForbiddenError","httpStatus":403,"severity":"error","filePath":"superset/daos/dashboard.py","lineNumber":485,"sourceCode":"    def favorited_ids(dashboards: list[Dashboard]) -> list[FavStar]:\n        ids = [dash.id for dash in dashboards]\n        return [\n            star.obj_id\n            for star in db.session.query(FavStar.obj_id)\n            .filter(\n                FavStar.class_name == FavStarClassName.DASHBOARD,\n                FavStar.obj_id.in_(ids),\n                FavStar.user_id == get_user_id(),\n            )\n            .all()\n        ]\n\n    @classmethod\n    def copy_dashboard(\n        cls, original_dash: Dashboard, data: dict[str, Any]\n    ) -> Dashboard:\n        if not security_manager.is_editor(original_dash):\n            raise DashboardForbiddenError()\n\n        dash = Dashboard()\n        # The copied dashboard and every chart cloned below share one creator,\n        # so both lookups are resolved here rather than inside the loop, where\n        # they would cost two extra queries for each chart in the dashboard.\n        creator_editors: list[Any] = []\n        creator_viewers: list[Any] = []\n        if g.user:\n            from superset.subjects.utils import (\n                get_default_viewers_for_new_asset,\n                get_user_subject,\n            )\n\n            user_subject = get_user_subject(g.user.id)\n            creator_editors = [user_subject] if user_subject else []\n            creator_viewers = get_default_viewers_for_new_asset(g.user.id)\n        dash.editors = creator_editors\n        dash.viewers = creator_viewers","sourceCodeStart":467,"sourceCodeEnd":503,"githubUrl":"https://github.com/apache/superset/blob/f4587218dd19d046c3e4d00063e7d27f8a2ed354/superset/daos/dashboard.py#L467-L503","documentation":"DashboardForbiddenError ('Changing this Dashboard is forbidden') raised at the top of DashboardDAO.copy_dashboard: security_manager.is_editor(original_dash) returned False. Duplicating a dashboard is treated as a write-ish operation requiring editor rights on the source, checked before any clone rows are created.","triggerScenarios":"POST to duplicate/copy a dashboard (dashboard copy API or UI 'Duplicate') as a user who can view but not edit the source dashboard — not an owner, not in its editor roles, and lacking a global can-write-dashboard capability.","commonSituations":"Viewers attempting to fork a shared dashboard; service accounts used for automation that were granted only read; ownership transferred away and the previous owner's role loses editor rights.","solutions":["Have an owner add the user (or their role) as an editor on the source dashboard, then retry the copy.","Ask an editor/admin to perform the duplication and transfer ownership of the clone.","If policy should allow viewer-forks, grant the role the write-dashboard capability — an operator decision, not a code fix."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"from superset import security_manager\n\ndef can_copy(user, dash) -> bool:\n    return bool(user is not None and security_manager.is_editor(dash))","typeGuard":null,"tryCatchPattern":"try:\n    clone = DashboardDAO.copy_dashboard(dash, data)\nexcept DashboardForbiddenError:\n    return jsonify({\"error\": \"editor rights on the source dashboard required\"}), 403","preventionTips":["Check security_manager.is_editor(dash) before offering a Duplicate action in UIs.","Grant editor roles on shared dashboards that teams are expected to fork.","Automation accounts that copy dashboards need write access, not just read."],"tags":["security","dashboard","permissions","copy","rbac"],"backgroundTag":null,"analyzedSha":"f4587218dd19d046c3e4d00063e7d27f8a2ed354","analyzedAt":"2026-08-14T22:39:27.425Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}