OrchardCMS/OrchardCore · error · InvalidOperationException

The 'Default' tenant can't be removed.

Error message

The 'Default' tenant can't be removed.

What it means

CheckCanRemoveShell throws InvalidOperationException because the Default tenant is the root shell of an Orchard Core application and can never be removed. The check runs before RemoveShellSettingsAsync/RemoveShellContextAsync perform any deletion.

Solutions

  1. Exclude the Default tenant before calling the remove APIs (check settings.IsDefaultShell() or settings.Name == "Default").
  2. If the goal is to reset Default, disable/reconfigure it instead of removing it.
  3. Fix the caller to pass the correct ShellSettings instance.

Example fix

// before
await _shellHost.RemoveShellSettingsAsync(settings);
// after
if (!settings.IsDefaultShell())
{
    await _shellHost.RemoveShellSettingsAsync(settings);
}
Defensive patterns

Strategy: validation

Validate before calling

if (settings.IsDefaultShell() || settings.Name == ShellSettings.DefaultShellName)
{
    throw new InvalidOperationException("The Default tenant cannot be removed.");
}

Prevention

When it happens

Trigger: Calling RemoveShellSettingsAsync or RemoveShellContextAsync with the settings of the 'Default' tenant (ShellSettings.DefaultShellName).

Common situations: Bulk tenant-cleanup scripts that iterate all tenants without excluding Default; admin UI/logic bugs passing the wrong tenant; recipes or migrations attempting to delete the Default tenant.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


AI-assisted analysis of OrchardCMS/OrchardCore@4306c0717f (2026-09-13). Data as JSON: /api/errors/f2cc0e5f33e3c59f. Report an issue: GitHub.

Appendix: source

Thrown at src/OrchardCore/OrchardCore/Shell/ShellHost.cs:517

        shellSettings.IsRunning() ||
        shellSettings.IsUninitialized() ||
        shellSettings.IsInitializing();

    /// <summary>
    /// Whether or not a shell can be released and removed from the list, false if disabled and still in use.
    /// Note: A disabled shell still in use will be released by its last scope, and keeping it in the list
    /// prevents a consumer from creating a new one that would have a null service provider.
    /// </summary>
    private bool CanReleaseShell(ShellSettings settings) => !settings.IsDisabled() || !IsShellActive(settings);

    /// <summary>
    /// Checks if a shell can be removed, throws an exception if the shell is neither uninitialized nor disabled.
    /// </summary>
    private void CheckCanRemoveShell(ShellSettings settings)
    {
        if (settings.IsDefaultShell())
        {
            throw new InvalidOperationException($"The '{ShellSettings.DefaultShellName}' tenant can't be removed.");
        }

        // A disabled shell may be still in use in at least one active scope.
        if (!settings.IsRemovable() || IsShellActive(settings))
        {
            throw new InvalidOperationException(
                $"The tenant '{settings.Name}' can't be removed as it is neither uninitialized nor disabled.");
        }
    }

    /// <summary>
    /// Whether or not a shell is in use in at least one active scope.
    /// </summary>
    private bool IsShellActive(ShellSettings settings) =>
        settings is { Name: not null } &&
        _shellContexts.TryGetValue(settings.Name, out var context) &&
        context.IsActive();

View on GitHub (pinned to 4306c0717f)