fullstackhero/dotnet-starter-kit · error · InvalidOperationException
Invalid Tenant
Error message
Invalid Tenant
What it means
AppTenantInfo.Activate enables a tenant (IsActive = true) but refuses to activate the root tenant, throwing InvalidOperationException("Invalid Tenant"). The root tenant is the system-level tenant and must never be switched to an active/normal state via this API.
Solutions
- Skip the root tenant when activating: if (tenant.Id != MultitenancyConstants.Root.Id) tenant.Activate();
- Filter the root tenant out of management queries/UI lists.
- Reject the root tenant id at the endpoint level with a clear 400 instead of letting domain code throw.
- Audit call sites like EnsureDemoTenantsExistAsync that loop tenants indiscriminately.
Example fix
// before
foreach (var t in tenants) t.Activate();
// after
foreach (var t in tenants.Where(t => t.Id != MultitenancyConstants.Root.Id))
t.Activate(); Defensive patterns
Strategy: validation
Validate before calling
if (tenant.Id == MultitenancyConstants.Root.Id)
throw new ArgumentException("The root tenant cannot be activated.");
tenant.Activate(); Type guard
bool IsActivatable(AppTenantInfo t) => t.Id != MultitenancyConstants.Root.Id && !t.IsActive;
Try / catch
try {
tenant.Activate();
} catch (InvalidOperationException ex) when (ex.Message == "Invalid Tenant") {
return Results.BadRequest("The root tenant cannot be activated.");
} Prevention
- Exclude MultitenancyConstants.Root.Id from all tenant-management lists and bulk loops.
- Reject the root tenant id in admin endpoints with a clear validation error.
- Add a unit test covering root-tenant activation/deactivation rejection.
When it happens
Trigger: Calling tenant.Activate() when tenant.Id equals MultitenancyConstants.Root.Id, e.g. iterating all tenants and activating each, or an admin endpoint receiving 'root' as the tenant id.
Common situations: Bulk activation scripts that don't exclude the root tenant; admin UI list that includes root; seeding or test fixtures activating the default tenant; user submitting the root tenant id in a management endpoint.
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
- Subscription cannot be backdated.
- Storage quota exceeded
- ConnectionString can't be null.
- Cross-tenant audit summary requires…
- Cross-tenant audit access requires…
AI-assisted analysis of fullstackhero/dotnet-starter-kit@3f2959e683 (2026-09-15).
Data as JSON: /api/errors/2ac08eba7916caf4.
Report an issue: GitHub.
Appendix: source
Thrown at src/BuildingBlocks/Shared/Multitenancy/AppTenantInfo.cs:65
/// <summary>Per-tenant quota overrides. Serialized as JSON by the tenant store; empty by default.</summary>
public Dictionary<QuotaResource, long> QuotaLimits { get; set; } = new();
public void AddValidity(int months) =>
ValidUpto = ValidUpto.AddMonths(months);
public void SetValidity(in DateTime validTill)
{
var normalized = validTill;
ValidUpto = ValidUpto < normalized
? normalized
: throw new InvalidOperationException("Subscription cannot be backdated.");
}
public void Activate()
{
if (Id == MultitenancyConstants.Root.Id)
{
throw new InvalidOperationException("Invalid Tenant");
}
IsActive = true;
}
public void Deactivate()
{
if (Id == MultitenancyConstants.Root.Id)
{
throw new InvalidOperationException("Invalid Tenant");
}
IsActive = false;
}
string? IAppTenantInfo.ConnectionString
{
get => ConnectionString;View on GitHub (pinned to 3f2959e683)