{"record":{"id":"71fe1639f14f1207","repo":"apache/superset","slug":"cannot-modify-system-themes","errorCode":null,"errorMessage":"Cannot modify system themes.","messagePattern":"Cannot modify system themes\\.","errorType":"exception","errorClass":"SystemThemeProtectedError","httpStatus":500,"severity":"error","filePath":"superset/commands/theme/delete.py","lineNumber":61,"sourceCode":"    def run(self) -> None:\n        self.validate()\n        assert self._models\n\n        # Dissociate dashboards from themes before deleting\n        self._dissociate_dashboards()\n\n        ThemeDAO.delete(self._models)\n\n    def validate(self) -> None:\n        # Validate/populate model exists\n        self._models = ThemeDAO.find_by_ids(self._model_ids)\n        if not self._models or len(self._models) != len(self._model_ids):\n            raise ThemeNotFoundError()\n\n        # Check if any of the themes are system themes\n        for theme in self._models:\n            if theme.is_system:\n                raise SystemThemeProtectedError()\n            # Check if theme is in use as system default or dark\n            if theme.is_system_default or theme.is_system_dark:\n                raise SystemThemeInUseError()\n\n        # Check for dashboard usage\n        self._dashboard_usage = self._get_dashboard_usage()\n\n    def _dissociate_dashboards(self) -> None:\n        \"\"\"Dissociate dashboards from themes before deletion.\"\"\"\n        from superset.models.dashboard import Dashboard\n\n        theme_ids = [theme.id for theme in self._models or []]\n        if not theme_ids:\n            return\n\n        # Get count of affected dashboards for logging\n        affected_count = (\n            db.session.query(Dashboard)","sourceCodeStart":43,"sourceCodeEnd":79,"githubUrl":"https://github.com/apache/superset/blob/f4587218dd19d046c3e4d00063e7d27f8a2ed354/superset/commands/theme/delete.py#L43-L79","documentation":"SystemThemeProtectedError raised by DeleteThemesCommand.validate when any theme in the bulk payload has is_system True. System themes (the built-in light/dark defaults shipped with Superset) are protected rows that can never be deleted, regardless of admin status — this is a data-integrity guard, not a permission check.","triggerScenarios":"Bulk delete including a theme flagged is_system — typically the seeded default themes created at install/upgrade time.","commonSituations":"'Select all and delete' in a theme management UI that includes built-in themes; attempting to clean up all themes before importing a fresh set; scripts that assume every theme is user-created.","solutions":["Filter is_system themes out of the delete payload — they are permanent.","Adjust UI/API callers to disable deletion affordances on system themes.","To change built-in look and feel, override the system default/dark selection instead of deleting the theme."],"exampleFix":"# before\nDeleteThemesCommand([theme.id for theme in all_themes]).run()\n# after\nDeleteThemesCommand([t.id for t in all_themes if not t.is_system]).run()","handlingStrategy":"type-guard","validationCode":"deletable = [t.id for t in themes if not t.is_system]\nif len(deletable) != len(themes):\n    warn_user(\"system themes cannot be deleted\")","typeGuard":"def is_deletable_theme(theme) -> bool:\n    return not theme.is_system and not theme.is_system_default and not theme.is_system_dark","tryCatchPattern":"try:\n    DeleteThemesCommand(ids).run()\nexcept (SystemThemeProtectedError, SystemThemeInUseError):\n    ids = [i for i in ids if i not in blocked_theme_ids()]\n    retry_with(ids)","preventionTips":["Filter is_system themes from delete payloads always.","Disable delete affordances on system themes in custom UIs.","Rebrand by redefining defaults, not by deleting built-in themes."],"tags":["themes","protected-resources","bulk-operations"],"backgroundTag":null,"analyzedSha":"f4587218dd19d046c3e4d00063e7d27f8a2ed354","analyzedAt":"2026-08-14T22:39:27.425Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}