fullstackhero/dotnet-starter-kit · error · CustomException
At least one active tenant is required.
Error message
At least one active tenant is required.
What it means
DeactivateAsync counts active tenants across the store and refuses when the deactivation would leave zero active tenants (tenantCount <= 1). At least one active tenant must always exist so the system keeps a usable default/root tenancy. Note the count is computed from GetAllAsync, so all tenants are considered, not just the one being deactivated.
Solutions
- Activate another tenant first so at least two are active, then deactivate the target.
- Re-create/activate the root tenant if it was inadvertently deactivated elsewhere.
- In automation, count active tenants before deactivating and stop at one.
- If this is a seeded dev environment, reseed tenants so more than one is active.
Example fix
// before
await tenantService.DeactivateAsync(lastActiveTenantId, ct);
// after
var all = await tenantService.GetAllAsync(ct);
var activeCount = all.Count(t => t.IsActive);
if (activeCount <= 1)
throw new InvalidOperationException("Activate another tenant before deactivating the last active one.");
await tenantService.DeactivateAsync(lastActiveTenantId, ct); Defensive patterns
Strategy: validation
Validate before calling
var all = await tenantService.GetAllAsync(ct);
if (all.Count(t => t.IsActive) <= 1)
throw new InvalidOperationException("Refusing: this would leave zero active tenants. Activate another first."); Type guard
bool CanDeactivate(IEnumerable<AppTenantInfo> all, string id) =>
all.Count(t => t.IsActive && !t.Id.Equals(id, StringComparison.OrdinalIgnoreCase)) >= 1; Try / catch
try
{
await tenantService.DeactivateAsync(id, ct);
}
catch (CustomException ex) when (ex.Message == "At least one active tenant is required.")
{
logger.LogWarning("Deactivation of {TenantId} blocked: last active tenant", id);
} Prevention
- Count active tenants before any deactivation batch and stop at one.
- Never script deactivation of all tenants; whitelist non-essential ones.
- Ensure seed/environments always contain more than one active tenant for testing offboarding.
- Check the active-count invariant after bulk operations to catch drift.
When it happens
Trigger: Deactivating the last remaining active tenant; deactivating the root tenant when it is the only active one (count check fires before the root check); environments where all other tenants were already deactivated or deleted.
Common situations: Clean-up scripts deactivating tenants one by one until only one remains; test/seed environments with a single tenant; tenant offboarding that didn't account for the always-one-active invariant.
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
- tenant is already activated
- tenant is already deactivated
- Not Found.
- Storage quota exceeded
- Subscription cannot be backdated.
AI-assisted analysis of fullstackhero/dotnet-starter-kit@3f2959e683 (2026-09-15).
Data as JSON: /api/errors/14ad4d4f8d04a359.
Report an issue: GitHub.
Appendix: source
Thrown at src/Modules/Multitenancy/Modules.Multitenancy/Services/TenantService.cs:132
foreach (var initializer in scope.ServiceProvider.GetServices<IDbInitializer>())
{
await initializer.SeedAsync(cancellationToken).ConfigureAwait(false);
}
}
public async Task<string> DeactivateAsync(string id, CancellationToken cancellationToken = default)
{
var tenant = await GetTenantInfoAsync(id, cancellationToken).ConfigureAwait(false);
if (!tenant.IsActive)
{
throw new CustomException($"tenant {id} is already deactivated");
}
int tenantCount = (await _tenantStore.GetAllAsync().ConfigureAwait(false)).Count(t => t.IsActive);
if (tenantCount <= 1)
{
throw new CustomException("At least one active tenant is required.");
}
if (tenant.Id.Equals(MultitenancyConstants.Root.Id, StringComparison.OrdinalIgnoreCase))
{
throw new CustomException("The root tenant cannot be deactivated.");
}
tenant.Deactivate();
await _tenantStore.UpdateAsync(tenant).ConfigureAwait(false);
await RefreshTenantCacheAsync(tenant).ConfigureAwait(false);
return $"tenant {id} is now deactivated";
}
public async Task<bool> ExistsWithIdAsync(string id, CancellationToken cancellationToken = default) =>
await _tenantStore.GetAsync(id).ConfigureAwait(false) is not null;
public async Task<bool> ExistsWithNameAsync(string name, CancellationToken cancellationToken = default) =>
(await _tenantStore.GetAllAsync().ConfigureAwait(false)).Any(t => t.Name == name);View on GitHub (pinned to 3f2959e683)