{"record":{"id":"2236e07a14840184","repo":"fullstackhero/dotnet-starter-kit","slug":"tenant-id-is-already-activated","errorCode":null,"errorMessage":"tenant {id} is already activated","messagePattern":"tenant (.+?) is already activated","errorType":"exception","errorClass":"CustomException","httpStatus":400,"severity":"warning","filePath":"src/Modules/Multitenancy/Modules.Multitenancy/Services/TenantService.cs","lineNumber":59,"sourceCode":"        ArgumentNullException.ThrowIfNull(config);\n        ArgumentNullException.ThrowIfNull(billingOptions);\n        _tenantStore = tenantStore;\n        _config = config.Value;\n        _serviceProvider = serviceProvider;\n        _dbContext = dbContext;\n        _provisioningService = provisioningService;\n        _timeProvider = timeProvider;\n        _billingOptions = billingOptions.Value;\n        _logger = logger;\n    }\n\n    public async Task<string> ActivateAsync(string id, CancellationToken cancellationToken)\n    {\n        var tenant = await GetTenantInfoAsync(id, cancellationToken).ConfigureAwait(false);\n\n        if (tenant.IsActive)\n        {\n            throw new CustomException($\"tenant {id} is already activated\");\n        }\n\n        await _provisioningService.EnsureCanActivateAsync(id, cancellationToken).ConfigureAwait(false);\n\n        tenant.Activate();\n\n        await _tenantStore.UpdateAsync(tenant).ConfigureAwait(false);\n        await RefreshTenantCacheAsync(tenant).ConfigureAwait(false);\n\n        return $\"tenant {id} is now activated\";\n    }\n\n    public async Task<string> CreateAsync(string id,\n        string name,\n        string? connectionString,\n        string adminEmail, string? issuer, string planKey, DateTime validUpto, CancellationToken cancellationToken)\n    {\n        if (connectionString?.Trim() == _config.ConnectionString.Trim())","sourceCodeStart":41,"sourceCodeEnd":77,"githubUrl":"https://github.com/fullstackhero/dotnet-starter-kit/blob/3f2959e683e9f83f13e55e1678c9119f63c7e8e5/src/Modules/Multitenancy/Modules.Multitenancy/Services/TenantService.cs#L41-L77","documentation":"TenantService.ActivateAsync throws a CustomException when the tenant's IsActive flag is already true, since activating an active tenant is a no-op conflict. The check runs before provisioning validation (EnsureCanActivateAsync) and before tenant.Activate() persists the state change.","triggerScenarios":"Calling ActivateAsync on a tenant whose IsActive is true; retrying an activation that already succeeded; concurrent activation requests from two clients; activating a tenant that was created already-active.","commonSituations":"Double-clicking 'Activate'; an automation pipeline that activates unconditionally on each run; an earlier activation partially succeeded (tenant flipped active but later steps failed), so re-running hits this guard.","solutions":["Check tenant.IsActive before calling ActivateAsync and skip when already active.","Treat this error as success in idempotent automation (catch CustomException and continue).","If activation previously failed after flipping IsActive, fix the downstream step instead of re-activating.","Serialize activation calls per tenant to avoid concurrent duplicate requests."],"exampleFix":"// before\nawait tenantService.ActivateAsync(tenantId, ct);\n\n// after\nvar tenant = await tenantService.GetAsync(tenantId, ct);\nif (!tenant.IsActive)\n{\n    await tenantService.ActivateAsync(tenantId, ct);\n}","handlingStrategy":"validation","validationCode":"var tenant = await tenantService.GetAsync(id, ct);\nif (tenant.IsActive) return; // nothing to do","typeGuard":"bool NeedsActivation(AppTenantInfo t) => !t.IsActive;","tryCatchPattern":"try\n{\n    await tenantService.ActivateAsync(id, ct);\n}\ncatch (CustomException ex) when (ex.Message.Contains(\"already activated\"))\n{\n    // idempotent success\n}","preventionTips":["Read tenant state before mutating; skip no-op transitions.","Design activation jobs to be idempotent — already-active is success.","Debounce/duplicate-suppress activate requests in the UI.","Check whether an earlier partial activation left the tenant active before retrying."],"tags":["multitenancy","tenant","idempotency","conflict"],"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"}