apache/superset · error · SystemThemeProtectedError
Cannot modify system themes.
Error message
Cannot modify system themes.
What it means
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.
Source
Thrown at superset/commands/theme/delete.py:61
def run(self) -> None:
self.validate()
assert self._models
# Dissociate dashboards from themes before deleting
self._dissociate_dashboards()
ThemeDAO.delete(self._models)
def validate(self) -> None:
# Validate/populate model exists
self._models = ThemeDAO.find_by_ids(self._model_ids)
if not self._models or len(self._models) != len(self._model_ids):
raise ThemeNotFoundError()
# Check if any of the themes are system themes
for theme in self._models:
if theme.is_system:
raise SystemThemeProtectedError()
# Check if theme is in use as system default or dark
if theme.is_system_default or theme.is_system_dark:
raise SystemThemeInUseError()
# Check for dashboard usage
self._dashboard_usage = self._get_dashboard_usage()
def _dissociate_dashboards(self) -> None:
"""Dissociate dashboards from themes before deletion."""
from superset.models.dashboard import Dashboard
theme_ids = [theme.id for theme in self._models or []]
if not theme_ids:
return
# Get count of affected dashboards for logging
affected_count = (
db.session.query(Dashboard)View on GitHub (pinned to f4587218dd)
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.
Example fix
# before DeleteThemesCommand([theme.id for theme in all_themes]).run() # after DeleteThemesCommand([t.id for t in all_themes if not t.is_system]).run()
Defensive patterns
Strategy: type-guard
Validate before calling
deletable = [t.id for t in themes if not t.is_system]
if len(deletable) != len(themes):
warn_user("system themes cannot be deleted") Type guard
def is_deletable_theme(theme) -> bool:
return not theme.is_system and not theme.is_system_default and not theme.is_system_dark Try / catch
try:
DeleteThemesCommand(ids).run()
except (SystemThemeProtectedError, SystemThemeInUseError):
ids = [i for i in ids if i not in blocked_theme_ids()]
retry_with(ids) Prevention
- 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.
When it happens
Trigger: Bulk delete including a theme flagged is_system — typically the seeded default themes created at install/upgrade time.
Common situations: '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.
Related errors
- Cannot delete theme that is set as system default or dark th
- Dashboard not found.
- Theme doesn't exist and user doesn't have permission to crea
- Could not find a valid command to import file
AI-assisted analysis of apache/superset@f4587218dd (2026-08-14).
Data as JSON: /api/errors/71fe1639f14f1207.
Report an issue: GitHub.