{"record":{"id":"2ac08eba7916caf4","repo":"fullstackhero/dotnet-starter-kit","slug":"invalid-tenant","errorCode":null,"errorMessage":"Invalid Tenant","messagePattern":"Invalid Tenant","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"src/BuildingBlocks/Shared/Multitenancy/AppTenantInfo.cs","lineNumber":65,"sourceCode":"    /// <summary>Per-tenant quota overrides. Serialized as JSON by the tenant store; empty by default.</summary>\n    public Dictionary<QuotaResource, long> QuotaLimits { get; set; } = new();\n\n    public void AddValidity(int months) =>\n        ValidUpto = ValidUpto.AddMonths(months);\n\n    public void SetValidity(in DateTime validTill)\n    {\n        var normalized = validTill;\n        ValidUpto = ValidUpto < normalized\n            ? normalized\n            : throw new InvalidOperationException(\"Subscription cannot be backdated.\");\n    }\n\n    public void Activate()\n    {\n        if (Id == MultitenancyConstants.Root.Id)\n        {\n            throw new InvalidOperationException(\"Invalid Tenant\");\n        }\n\n        IsActive = true;\n    }\n\n    public void Deactivate()\n    {\n        if (Id == MultitenancyConstants.Root.Id)\n        {\n            throw new InvalidOperationException(\"Invalid Tenant\");\n        }\n\n        IsActive = false;\n    }\n\n    string? IAppTenantInfo.ConnectionString\n    {\n        get => ConnectionString;","sourceCodeStart":47,"sourceCodeEnd":83,"githubUrl":"https://github.com/fullstackhero/dotnet-starter-kit/blob/3f2959e683e9f83f13e55e1678c9119f63c7e8e5/src/BuildingBlocks/Shared/Multitenancy/AppTenantInfo.cs#L47-L83","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nforeach (var t in tenants) t.Activate();\n\n// after\nforeach (var t in tenants.Where(t => t.Id != MultitenancyConstants.Root.Id))\n    t.Activate();","handlingStrategy":"validation","validationCode":"if (tenant.Id == MultitenancyConstants.Root.Id)\n    throw new ArgumentException(\"The root tenant cannot be activated.\");\ntenant.Activate();","typeGuard":"bool IsActivatable(AppTenantInfo t) => t.Id != MultitenancyConstants.Root.Id && !t.IsActive;","tryCatchPattern":"try {\n    tenant.Activate();\n} catch (InvalidOperationException ex) when (ex.Message == \"Invalid Tenant\") {\n    return Results.BadRequest(\"The root tenant cannot be activated.\");\n}","preventionTips":["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."],"tags":["multitenancy","domain-logic","tenant-management"],"backgroundTag":"invalid-state-transition","analyzedSha":"3f2959e683e9f83f13e55e1678c9119f63c7e8e5","analyzedAt":"2026-09-15T22:20:53.684Z","contentChangedAt":"2026-09-15T22:20:53.684Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}