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 enabled. What it means
ThemeService.EnableThemeFeaturesAsync builds a stack of the theme and its base themes to enable features in order. If the same theme name is encountered twice while walking the base chain (cycle or self-reference in BaseTheme), it throws InvalidOperationException to prevent an infinite loop.
Solutions
- Correct the BaseTheme reference in the theme manifest/ThemeAttribute so the chain is acyclic and terminates.
- Set BaseTheme to a well-known base (e.g. safe mode or TheTheme) or remove it.
- Rebuild and redeploy the corrected theme, then retry enabling.
Example fix
// before [Theme(Name = "MyTheme", BaseTheme = "MyChildTheme")] // cycle // after [Theme(Name = "MyTheme", BaseTheme = "TheTheme")]
Defensive patterns
Strategy: validation
Validate before calling
// detect cycles before enabling
var visited = new HashSet<string>();
for (var t = themeName; t != null; t = extensionManager.GetExtension(t)?.BaseTheme) {
if (!visited.Add(t)) throw new InvalidOperationException($"Cycle: '{t}' appears twice");
} Type guard
const isAcyclicTheme = (name, baseOf) => { const seen = new Set(); for (let t = name; t; t = baseOf(t)) { if (seen.has(t)) return false; seen.add(t); } return true; }; Try / catch
try {
await themeService.EnableThemeFeaturesAsync(themeName);
} catch (InvalidOperationException ex) when (ex.Message.Contains("already in the stack")) {
logger.LogError(ex, "Base theme cycle while enabling {Theme}", themeName);
} Prevention
- Keep base-theme chains linear and terminating in a built-in theme.
- Validate ThemeAttribute/manifest BaseTheme values at build time.
- Re-check manifests after theme renames to avoid stale references.
When it happens
Trigger: Calling EnableThemeFeaturesAsync for a theme whose base-theme chain contains a cycle (A->B->A) or self-reference — i.e. the same theme pushed onto the stack twice.
Common situations: Custom theme declaring itself or a descendant as its BaseTheme in ThemeAttribute/manifest; mutually-referencing themes; renamed themes leaving stale BaseTheme values pointing at each other.
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/2b5a24088a94cdcb.
Report an issue: GitHub.
Appendix: source
Thrown at src/OrchardCore.Modules/OrchardCore.Themes/Services/ThemeService.cs:76
{
var themeId = themes.Dequeue();
// Not disabling base theme if it's the current theme.
if (themeId != currentTheme)
{
await DisableFeaturesAsync(new[] { themeId }, true);
}
}
}
public async Task EnableThemeFeaturesAsync(string themeName)
{
var themes = new Stack<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 enabled.", themeName].ToString());
}
themes.Push(themeName);
// TODO: MWP: probably follow on issue: should this be recursive? maybe with a depth limit? i.e. base3->base2->base1 ...
var extensionInfo = _extensionManager.GetExtension(themeName);
var theme = new ThemeExtensionInfo(extensionInfo);
// Along similar lines as with ModuleAttribute, internal safety checks handled within the attribute itself
themeName = theme.BaseTheme;
}
while (themes.Count > 0)
{
var themeId = themes.Pop();
await EnableFeaturesAsync(new[] { themeId }, true);
}
}View on GitHub (pinned to 4306c0717f)