fullstackhero/dotnet-starter-kit · warning · CustomException
tenant is already activated
Error message
tenant {id} is already activated What it means
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.
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.
Example fix
// before
await tenantService.ActivateAsync(tenantId, ct);
// after
var tenant = await tenantService.GetAsync(tenantId, ct);
if (!tenant.IsActive)
{
await tenantService.ActivateAsync(tenantId, ct);
} Defensive patterns
Strategy: validation
Validate before calling
var tenant = await tenantService.GetAsync(id, ct); if (tenant.IsActive) return; // nothing to do
Type guard
bool NeedsActivation(AppTenantInfo t) => !t.IsActive;
Try / catch
try
{
await tenantService.ActivateAsync(id, ct);
}
catch (CustomException ex) when (ex.Message.Contains("already activated"))
{
// idempotent success
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
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
- tenant is already deactivated
- file already finalized
- Provisioning already running for tenant
- At least one active tenant is required.
- Not Found.
AI-assisted analysis of fullstackhero/dotnet-starter-kit@3f2959e683 (2026-09-15).
Data as JSON: /api/errors/2236e07a14840184.
Report an issue: GitHub.
Appendix: source
Thrown at src/Modules/Multitenancy/Modules.Multitenancy/Services/TenantService.cs:59
ArgumentNullException.ThrowIfNull(config);
ArgumentNullException.ThrowIfNull(billingOptions);
_tenantStore = tenantStore;
_config = config.Value;
_serviceProvider = serviceProvider;
_dbContext = dbContext;
_provisioningService = provisioningService;
_timeProvider = timeProvider;
_billingOptions = billingOptions.Value;
_logger = logger;
}
public async Task<string> ActivateAsync(string id, CancellationToken cancellationToken)
{
var tenant = await GetTenantInfoAsync(id, cancellationToken).ConfigureAwait(false);
if (tenant.IsActive)
{
throw new CustomException($"tenant {id} is already activated");
}
await _provisioningService.EnsureCanActivateAsync(id, cancellationToken).ConfigureAwait(false);
tenant.Activate();
await _tenantStore.UpdateAsync(tenant).ConfigureAwait(false);
await RefreshTenantCacheAsync(tenant).ConfigureAwait(false);
return $"tenant {id} is now activated";
}
public async Task<string> CreateAsync(string id,
string name,
string? connectionString,
string adminEmail, string? issuer, string planKey, DateTime validUpto, CancellationToken cancellationToken)
{
if (connectionString?.Trim() == _config.ConnectionString.Trim())View on GitHub (pinned to 3f2959e683)