fullstackhero/dotnet-starter-kit · error · CustomException
Tenant is not provisioned. Status: .
Error message
Tenant {tenantId} is not provisioned. Status: {provisioning.Status}. What it means
EnsureCanActivateAsync guards tenant activation: if the latest provisioning run is not Completed, activation is rejected with a CustomException stating the current provisioning status. It prevents activating tenants whose database/steps provisioning is incomplete. GetLatest is skipped only when no provisioning record exists (null short-circuits to allowed per the preceding check).
Solutions
- Check GetStatusAsync for the tenant and wait until Status == Completed before activating.
- If Status is Failed, inspect the TenantProvisioningStep records for the failing step, fix the root cause, then call RetryAsync and activate after completion.
- If Status is stuck Pending/Running, resolve the hung/stale job (mark Failed, retry) so a new run can complete.
- Sequence automation: provision -> poll until Completed -> activate.
Example fix
// before
await tenantService.ActivateAsync(tenantId, ct);
// after
var status = await provisioningService.GetStatusAsync(tenantId, ct);
if (status.Status != TenantProvisioningStatus.Completed)
throw new InvalidOperationException($"Wait for provisioning to complete (currently {status.Status}).");
await tenantService.ActivateAsync(tenantId, ct); Defensive patterns
Strategy: validation
Validate before calling
var status = await provisioningService.GetStatusAsync(tenantId, ct);
if (status.Status != TenantProvisioningStatus.Completed)
throw new InvalidOperationException($"Cannot activate: provisioning status is {status.Status}."); Type guard
bool CanActivate(TenantProvisioningStatusDto s) => s.Status == TenantProvisioningStatus.Completed;
Try / catch
try
{
await tenantService.ActivateAsync(tenantId, ct);
}
catch (CustomException ex) when (ex.Message.Contains("is not provisioned"))
{
var s = await provisioningService.GetStatusAsync(tenantId, ct);
logger.LogWarning("Activation blocked for {TenantId}, provisioning status: {Status}", tenantId, s.Status);
} Prevention
- Automate provision -> wait for Completed -> activate as a pipeline, never manual activation early.
- Alert on Failed provisioning steps and fix before attempting activation.
- Surface provisioning status next to the Activate button in the admin UI.
- Watch for Pending/Running records that never transition (hung Hangfire jobs).
When it happens
Trigger: Calling TenantService.ActivateAsync for a tenant whose latest TenantProvisioning.Status is Pending, Running, or Failed. Also happens when a provisioning run failed partway (e.g. database step failed) and the admin tries to activate the tenant anyway.
Common situations: Provisioning job still in progress while an admin activates early; a failed migration step left status Failed; environment where Hangfire never ran the job so status remains Pending.
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 not found during provisioning.
- Tenant not found for provisioning.
- Provisioning already running for tenant
- Provisioning not found for tenant
- Provisioning for tenant not found.
AI-assisted analysis of fullstackhero/dotnet-starter-kit@3f2959e683 (2026-09-15).
Data as JSON: /api/errors/fb0ae4cb2a567b01.
Report an issue: GitHub.
Appendix: source
Thrown at src/Modules/Multitenancy/Modules.Multitenancy/Provisioning/TenantProvisioningService.cs:103
public async Task<TenantProvisioningStatusDto> GetStatusAsync(string tenantId, CancellationToken cancellationToken)
{
var provisioning = await GetLatestAsync(tenantId, cancellationToken).ConfigureAwait(false)
?? throw new NotFoundException($"Provisioning not found for tenant {tenantId}.");
return ToDto(provisioning);
}
public async Task EnsureCanActivateAsync(string tenantId, CancellationToken cancellationToken)
{
var provisioning = await GetLatestAsync(tenantId, cancellationToken).ConfigureAwait(false);
if (provisioning is null)
{
return;
}
if (provisioning.Status != TenantProvisioningStatus.Completed)
{
throw new CustomException($"Tenant {tenantId} is not provisioned. Status: {provisioning.Status}.");
}
}
public async Task<string> RetryAsync(string tenantId, CancellationToken cancellationToken)
{
var provisioning = await StartAsync(tenantId, cancellationToken).ConfigureAwait(false);
return provisioning.CorrelationId;
}
public async Task<bool> MarkRunningAsync(string tenantId, string correlationId, TenantProvisioningStepName step, CancellationToken cancellationToken)
{
var provisioning = await RequireAsync(tenantId, correlationId, cancellationToken).ConfigureAwait(false);
var stepEntity = provisioning.Steps.First(s => s.Step == step);
if (stepEntity.Status == TenantProvisioningStatus.Completed)
{
return false;
}View on GitHub (pinned to 3f2959e683)