{"record":{"id":"73a720ef3493c4d7","repo":"fullstackhero/dotnet-starter-kit","slug":"tenant-must-have-at-least-one-active-administrator","errorCode":null,"errorMessage":"Tenant must have at least one active administrator.","messagePattern":"Tenant must have at least one active administrator\\.","errorType":"exception","errorClass":"CustomException","httpStatus":400,"severity":"error","filePath":"src/Modules/Identity/Modules.Identity/Services/UserStatusService.cs","lineNumber":110,"sourceCode":"            await AuditPolicyFailureAsync(context, \"AdminDeactivationBlocked\", cancellationToken);\n            throw new CustomException(\"Administrators cannot be deactivated.\", Array.Empty<string>(), HttpStatusCode.BadRequest);\n        }\n\n        if (!context.ActivateUser)\n        {\n            await EnsureMinimumActiveAdminsAsync(context, cancellationToken);\n        }\n    }\n\n    private async Task EnsureMinimumActiveAdminsAsync(\n        ToggleStatusContext context,\n        CancellationToken cancellationToken)\n    {\n        var activeAdmins = await userManager.GetUsersInRoleAsync(RoleConstants.Admin);\n        if (!activeAdmins.Any(u => u.IsActive))\n        {\n            await AuditPolicyFailureAsync(context, \"NoActiveAdmins\", cancellationToken);\n            throw new CustomException(\"Tenant must have at least one active administrator.\", Array.Empty<string>(), HttpStatusCode.BadRequest);\n        }\n    }\n\n    private static void ApplyStatusChange(ToggleStatusContext context)\n    {\n        if (context.ActivateUser)\n        {\n            context.TargetUser.Activate(context.ActorId.ToString(), context.TenantId);\n        }\n        else\n        {\n            context.TargetUser.Deactivate(context.ActorId.ToString(), \"Status toggled by administrator\", context.TenantId);\n        }\n    }\n\n    private async Task SaveAndAuditAsync(\n        ToggleStatusContext context,\n        CancellationToken cancellationToken)","sourceCodeStart":92,"sourceCodeEnd":128,"githubUrl":"https://github.com/fullstackhero/dotnet-starter-kit/blob/3f2959e683e9f83f13e55e1678c9119f63c7e8e5/src/Modules/Identity/Modules.Identity/Services/UserStatusService.cs#L92-L128","documentation":"UserStatusService.EnsureMinimumActiveAdminsAsync throws CustomException with 400 BadRequest (\"Tenant must have at least one active administrator.\") when deactivating the target would leave the tenant with zero active admin users. It fetches all users in the Admin role and verifies at least one remains IsActive; audited as NoActiveAdmins first.","triggerScenarios":"Deactivating the last remaining active administrator of a tenant, i.e. after this change no user in the Admin role would remain IsActive.","commonSituations":"Tenants where other admins are already inactive or their Admin role was stripped; bulk deactivation jobs that would drain all admins; freshly created tenants with a single seeded admin.","solutions":["Activate another user and grant them the Admin role before deactivating this one.","Reactivate an existing inactive admin first.","Skip the last active admin in bulk deactivation jobs.","Keep at least two active admins per tenant as an operational rule."],"exampleFix":"// before\nawait mediator.Send(new ToggleUserStatusCommand { UserId = lastAdminId, ActivateUser = false });\n// after\nvar newAdmin = await userManager.FindByEmailAsync(\"ops@tenant.com\");\nawait userManager.AddToRoleAsync(newAdmin, RoleConstants.Admin);\nawait mediator.Send(new ToggleUserStatusCommand { UserId = lastAdminId, ActivateUser = false });","handlingStrategy":"validation","validationCode":"var admins = await userManager.GetUsersInRoleAsync(RoleConstants.Admin);\nif (!admins.Any(u => u.IsActive && u.Id != targetId))\n    return Result.BadRequest(\"Promote another active admin first.\");","typeGuard":null,"tryCatchPattern":"try { ... }\ncatch (CustomException) { showBanner(\"Tenant needs at least one active administrator.\"); }","preventionTips":["Maintain redundancy: at least two active admins per tenant.","Pre-check the remaining-admin count in deactivation workflows.","Monitor tenant admin counts with a recurring job/report.","Handle the error gracefully in bulk operations by skipping the last admin."],"tags":["identity","policy","tenant","admin-protection"],"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"}