OrchardCMS/OrchardCore · error · InvalidOperationException
The theme " " is already in the stack of themes that need…
Error message
The theme "{0}" is already in the stack of themes that need features disabled. What it means
ThemeService.DisableThemeFeaturesAsync walks the base-theme chain of the theme being disabled using a queue of theme names. If a theme name appears twice in that chain (a cycle or self-reference in the BaseTheme dependency), it throws InvalidOperationException to prevent infinite recursion.
Solutions
- Fix the BaseTheme declaration in the theme's manifest/ThemeAttribute so the chain terminates (no self- or mutual reference).
- Set the offending theme's base theme to null or a valid acyclic parent theme.
- Rebuild/redeploy the theme with the corrected manifest, then retry disabling.
Example fix
// before (theme manifest) "BaseTheme": "MyTheme" // self-reference // after "BaseTheme": "TheTheme" // or removed entirely
Defensive patterns
Strategy: validation
Validate before calling
// verify base-theme chain is acyclic before disabling
var seen = new HashSet<string>();
for (var t = themeName; t != null; t = extensionManager.GetExtension(t)?.BaseTheme) {
if (!seen.Add(t)) throw new InvalidOperationException($"Base theme cycle at '{t}'");
} Type guard
const hasAcyclicBaseChain = (name, getBase) => { const s = new Set(); for (let t = name; t; t = getBase(t)) { if (s.has(t)) return false; s.add(t); } return true; }; Try / catch
try {
await themeService.DisableThemeFeaturesAsync(themeName);
} catch (InvalidOperationException ex) when (ex.Message.Contains("already in the stack")) {
logger.LogError(ex, "Base-theme cycle detected for {Theme}", themeName);
} Prevention
- Never set a theme's BaseTheme to itself or a descendant.
- Validate theme manifests in CI (walk base chains for cycles).
- Avoid manual edits of manifest BaseTheme values after renames.
When it happens
Trigger: Calling DisableThemeFeaturesAsync for a theme whose BaseTheme chain contains a cycle (theme A bases on B, B bases on A) or where the same theme name is already queued — typically a malformed Theme attribute in the module's manifest.
Common situations: A custom theme accidentally setting its own name as BaseTheme; two custom themes referencing each other as base themes; corrupted recipe/manifest data after renaming a theme.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
AI-assisted analysis of OrchardCMS/OrchardCore@4306c0717f (2026-09-13).
Data as JSON: /api/errors/eb4bfe97c96ad5c2.
Report an issue: GitHub.
Appendix: source
Thrown at src/OrchardCore.Modules/OrchardCore.Themes/Services/ThemeService.cs:39
IHtmlLocalizer<ThemeService> htmlLocalizer,
INotifier notifier)
{
_extensionManager = extensionManager;
_shellFeaturesManager = shellFeaturesManager;
_siteThemeService = siteThemeService;
_notifier = notifier;
H = htmlLocalizer;
}
public async Task DisableThemeFeaturesAsync(string themeName)
{
var themes = new Queue<string>();
while (themeName != null)
{
if (themes.Contains(themeName))
{
throw new InvalidOperationException(H["The theme \"{0}\" is already in the stack of themes that need features disabled.", themeName].ToString());
}
var theme = _extensionManager.GetExtension(themeName);
if (theme == null)
{
break;
}
themes.Enqueue(themeName);
themeName = !string.IsNullOrWhiteSpace(theme.Manifest.Name)
? theme.Manifest.Name
: null;
}
var currentTheme = await _siteThemeService.GetSiteThemeNameAsync();
while (themes.Count > 0)View on GitHub (pinned to 4306c0717f)