{"record":{"id":"bed90d44713a0b16","repo":"fullstackhero/dotnet-starter-kit","slug":"tenant-tenant-id-is-deactivated","errorCode":null,"errorMessage":"tenant {tenant.Id} is deactivated","messagePattern":"tenant (.+?) is deactivated","errorType":"exception","errorClass":"UnauthorizedException","httpStatus":401,"severity":"error","filePath":"src/Modules/Identity/Modules.Identity/Services/IdentityService.cs","lineNumber":286,"sourceCode":"            throw new UnauthorizedException(\"user is deactivated\");\n        }\n\n        if (!user.EmailConfirmed)\n        {\n            throw new UnauthorizedException(\"email not confirmed\");\n        }\n    }\n\n    private void ValidateTenantStatus(AppTenantInfo tenant)\n    {\n        if (tenant.Id == MultitenancyConstants.Root.Id)\n        {\n            return;\n        }\n\n        if (!tenant.IsActive)\n        {\n            throw new UnauthorizedException($\"tenant {tenant.Id} is deactivated\");\n        }\n\n        // Honor the billing grace period: a lapsed tenant can still authenticate until\n        // ValidUpto + grace (matching the request-time guard in MultitenancyModule).\n        if (_timeProvider.GetUtcNow().UtcDateTime > tenant.ValidUpto.AddDays(_gracePeriodDays))\n        {\n            throw new UnauthorizedException($\"tenant {tenant.Id} validity has expired\");\n        }\n    }\n\n    private async Task<List<Claim>> BuildUserClaimsAsync(FshUser user, string tenantId, CancellationToken ct)\n    {\n        var claims = CreateBasicClaims(user, tenantId);\n        await AddRoleClaimsAsync(claims, user, ct);\n        return claims;\n    }\n\n    private static List<Claim> CreateBasicClaims(FshUser user, string tenantId)","sourceCodeStart":268,"sourceCodeEnd":304,"githubUrl":"https://github.com/fullstackhero/dotnet-starter-kit/blob/3f2959e683e9f83f13e55e1678c9119f63c7e8e5/src/Modules/Identity/Modules.Identity/Services/IdentityService.cs#L268-L304","documentation":"ValidateTenantStatus throws UnauthorizedException with the tenant id in the message when the tenant record's IsActive flag is false. Root tenant is exempt. Called during credential login and refresh validation, a deactivated tenant blocks all of its users from obtaining or renewing tokens.","triggerScenarios":"Any login/refresh request carrying a tenant header whose tenant row has IsActive=false — usually after billing-driven deactivation, an admin disabling the tenant, or upgrade/seed restoring IsActive incorrectly.","commonSituations":"Subscription lapsed and tenant was deactivated pending renewal; SaaS operator disabled a tenant for abuse/maintenance; client config pointing at the wrong tenant that happens to be disabled; restored database where IsActive flags were reset.","solutions":["Re-enable the tenant: set IsActive=true on the tenant record (admin endpoint or tenants table).","If deactivation is billing-related, resolve the subscription and re-activate; note the separate grace-period expiry throws 'validity has expired' instead.","Verify the client is sending the intended tenant identifier — a typo may target a disabled tenant."],"exampleFix":"// before\nUPDATE tenants SET is_active = false WHERE id = 'acme';\n// after (after renewal confirmed)\nUPDATE tenants SET is_active = true WHERE id = 'acme';","handlingStrategy":"try-catch","validationCode":"// when the API exposes tenant info, check before authenticating\nconst tenant = await api.get(`/api/tenants/${tenantId}`);\nif (tenant && tenant.isActive === false) { showTenantDisabledScreen(); return; }","typeGuard":"function isActiveTenant(t: { id: string; isActive: boolean } | null | undefined): t is { id: string; isActive: true } {\n  return !!t && t.isActive === true && t.id.length > 0;\n}","tryCatchPattern":"catch (ApiError e) when (e.StatusCode === 401 && /tenant .* is deactivated/.test(e.Message)) {\n  showMessage('Your workspace is deactivated. Contact your account manager to reactivate.');\n  haltAuthRetryLoop();\n}","preventionTips":["Send the tenant identifier from validated configuration, not free-text input.","Notify tenant admins before/when a tenant is deactivated so clients stop retrying.","Track billing status and re-enable tenants promptly after renewal."],"tags":["auth","multitenancy","tenant-deactivated","http-401"],"backgroundTag":"permission-denied","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"}