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

  1. Fix the BaseTheme declaration in the theme's manifest/ThemeAttribute so the chain terminates (no self- or mutual reference).
  2. Set the offending theme's base theme to null or a valid acyclic parent theme.
  3. 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

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)