OrchardCMS/OrchardCore · error · InvalidOperationException
The tenant ' ' can't be removed as it is neither…
Error message
The tenant '{settings.Name}' can't be removed as it is neither uninitialized nor disabled. What it means
CheckCanRemoveShell throws InvalidOperationException when the target tenant is not removable: it is neither uninitialized nor disabled, or a shell for it is still active in at least one scope. Only uninitialized or disabled, inactive tenants may be deleted.
Solutions
- Disable the tenant first and wait for it to become inactive, then remove it.
- Retry after in-flight requests complete — active scopes release the shell when requests finish.
- Check settings.IsRemovable() and ensure no active scopes exist before attempting removal.
Example fix
// before await _shellHost.RemoveShellSettingsAsync(settings); // after settings.State = TenantState.Disabled; await _shellHost.UpdateShellSettingsAsync(settings); // ensure no active scopes, then: await _shellHost.RemoveShellSettingsAsync(settings);
Defensive patterns
Strategy: validation
Validate before calling
if (!settings.IsRemovable() || settings.Name == ShellSettings.DefaultShellName)
{
// disable first, then remove after in-flight requests drain
settings.State = TenantState.Disabled;
await shellHost.UpdateShellSettingsAsync(settings);
} Prevention
- Call settings.IsRemovable() before RemoveShellSettingsAsync/RemoveShellContextAsync.
- Disable the tenant and let active scopes drain before deletion.
- Avoid removing tenants while requests against them may still be executing.
When it happens
Trigger: Calling RemoveShellSettingsAsync/RemoveShellContextAsync for a running (enabled and started) tenant, or for a disabled tenant whose shell context is still held by active scopes.
Common situations: Deleting a live tenant from a script without disabling it first; removing a tenant that was just disabled but still serves in-flight requests; UI logic racing with active request scopes holding the shell.
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
- The 'Default' tenant can't be removed.
- Unable to reload the tenant
- Unexpected shell state for
- File path must be a non-empty string.
- Can't resolve a scope on tenant
AI-assisted analysis of OrchardCMS/OrchardCore@4306c0717f (2026-09-13).
Data as JSON: /api/errors/e7fe4481040752be.
Report an issue: GitHub.
Appendix: source
Thrown at src/OrchardCore/OrchardCore/Shell/ShellHost.cs:523
/// 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();
public void Dispose()
{
foreach (var shell in ListShellContexts())
{
shell.Dispose();
}View on GitHub (pinned to 4306c0717f)