OrchardCMS/OrchardCore · error · InvalidOperationException
Unexpected shell state for
Error message
Unexpected shell state for {settings.Name} What it means
CreateShellContextAsync throws InvalidOperationException when the tenant's ShellSettings state does not allow a shell context to be created — the settings are in a state not handled by the factory branches above the throw. This is an internal invariant: the shell state machine reached an unhandled combination.
Solutions
- Log settings.Name and its State value to identify which state reached the unhandled branch.
- Ensure tenant state changes go through ShellHost APIs instead of mutating ShellSettings directly.
- Avoid removing or disabling tenants while traffic is in flight; quiesce requests first.
- Retry after the concurrent state transition completes — this is often a race, not a persistent condition.
Defensive patterns
Strategy: try-catch
Validate before calling
// Inspect the tenant state before requesting a shell context
if (settings.State is not (TenantState.Running or TenantState.Uninitialized or TenantState.Building))
{
_logger.LogWarning("Tenant {Name} in unexpected state {State}; skipping shell creation.", settings.Name, settings.State);
return;
} Try / catch
try
{
var context = await _shellHost.GetOrCreateShellContextAsync(settings);
}
catch (InvalidOperationException ex) when (ex.Message.StartsWith("Unexpected shell state"))
{
_logger.LogWarning(ex, "Shell state race for tenant {Name}; retrying.", settings.Name);
// retry once after the concurrent transition completes
} Prevention
- Change tenant state only via ShellHost/ShellSettingsManager APIs, never by mutating ShellSettings directly.
- Avoid removing/disabling tenants during live request processing.
- Log ShellSettings.State on failure to diagnose which state reached the unhandled branch.
When it happens
Trigger: Requesting a shell context (GetOrCreateShellContextAsync) for a tenant whose ShellSettings state is unexpected — e.g. settings exist but their state flag was changed concurrently or the tenant is in an intermediate/degenerate state not covered by the factory logic.
Common situations: Concurrent modification of ShellSettings while requests are being served; a tenant being removed or state-transitioned during request handling; custom code that mutates ShellSettings.State directly.
Understand the failure class
Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.
Related errors
- Unable to reload the tenant
- The 'Default' feature is not registered.
- The 'Default' tenant can't be removed.
- The tenant ' ' can't be removed as it is neither…
- Can't resolve a scope on tenant
AI-assisted analysis of OrchardCMS/OrchardCore@4306c0717f (2026-09-13).
Data as JSON: /api/errors/da2c11403e6c2f07.
Report an issue: GitHub.
Appendix: source
Thrown at src/OrchardCore/OrchardCore/Shell/ShellHost.cs:410
if (_logger.IsEnabled(LogLevel.Debug))
{
_logger.LogDebug("Creating disabled shell context for tenant '{TenantName}'", settings.Name);
}
return Task.FromResult(new ShellContext { Settings = settings });
}
else if (settings.IsRunning() || settings.IsInitializing())
{
if (_logger.IsEnabled(LogLevel.Debug))
{
_logger.LogDebug("Creating shell context for tenant '{TenantName}'", settings.Name);
}
return _shellContextFactory.CreateShellContextAsync(settings);
}
else
{
throw new InvalidOperationException("Unexpected shell state for " + settings.Name);
}
}
/// <summary>
/// Creates a transient shell for the default tenant's setup.
/// </summary>
private async Task<ShellContext> CreateSetupContextAsync(ShellSettings defaultSettings)
{
if (_logger.IsEnabled(LogLevel.Debug))
{
_logger.LogDebug("Creating shell context for root setup.");
}
if (defaultSettings is null)
{
// Creates a default shell settings based on the configuration.
defaultSettings = _shellSettingsManager
.CreateDefaultSettings()View on GitHub (pinned to 4306c0717f)