{"record":{"id":"fb0ae4cb2a567b01","repo":"fullstackhero/dotnet-starter-kit","slug":"tenant-tenantid-is-not-provisioned-status-provisioning","errorCode":null,"errorMessage":"Tenant {tenantId} is not provisioned. Status: {provisioning.Status}.","messagePattern":"Tenant (.+?) is not provisioned\\. Status: (.+?)\\.","errorType":"exception","errorClass":"CustomException","httpStatus":400,"severity":"error","filePath":"src/Modules/Multitenancy/Modules.Multitenancy/Provisioning/TenantProvisioningService.cs","lineNumber":103,"sourceCode":"    public async Task<TenantProvisioningStatusDto> GetStatusAsync(string tenantId, CancellationToken cancellationToken)\n    {\n        var provisioning = await GetLatestAsync(tenantId, cancellationToken).ConfigureAwait(false)\n            ?? throw new NotFoundException($\"Provisioning not found for tenant {tenantId}.\");\n\n        return ToDto(provisioning);\n    }\n\n    public async Task EnsureCanActivateAsync(string tenantId, CancellationToken cancellationToken)\n    {\n        var provisioning = await GetLatestAsync(tenantId, cancellationToken).ConfigureAwait(false);\n        if (provisioning is null)\n        {\n            return;\n        }\n\n        if (provisioning.Status != TenantProvisioningStatus.Completed)\n        {\n            throw new CustomException($\"Tenant {tenantId} is not provisioned. Status: {provisioning.Status}.\");\n        }\n    }\n\n    public async Task<string> RetryAsync(string tenantId, CancellationToken cancellationToken)\n    {\n        var provisioning = await StartAsync(tenantId, cancellationToken).ConfigureAwait(false);\n        return provisioning.CorrelationId;\n    }\n\n    public async Task<bool> MarkRunningAsync(string tenantId, string correlationId, TenantProvisioningStepName step, CancellationToken cancellationToken)\n    {\n        var provisioning = await RequireAsync(tenantId, correlationId, cancellationToken).ConfigureAwait(false);\n        var stepEntity = provisioning.Steps.First(s => s.Step == step);\n\n        if (stepEntity.Status == TenantProvisioningStatus.Completed)\n        {\n            return false;\n        }","sourceCodeStart":85,"sourceCodeEnd":121,"githubUrl":"https://github.com/fullstackhero/dotnet-starter-kit/blob/3f2959e683e9f83f13e55e1678c9119f63c7e8e5/src/Modules/Multitenancy/Modules.Multitenancy/Provisioning/TenantProvisioningService.cs#L85-L121","documentation":"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).","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nawait tenantService.ActivateAsync(tenantId, ct);\n\n// after\nvar status = await provisioningService.GetStatusAsync(tenantId, ct);\nif (status.Status != TenantProvisioningStatus.Completed)\n    throw new InvalidOperationException($\"Wait for provisioning to complete (currently {status.Status}).\");\nawait tenantService.ActivateAsync(tenantId, ct);","handlingStrategy":"validation","validationCode":"var status = await provisioningService.GetStatusAsync(tenantId, ct);\nif (status.Status != TenantProvisioningStatus.Completed)\n    throw new InvalidOperationException($\"Cannot activate: provisioning status is {status.Status}.\");","typeGuard":"bool CanActivate(TenantProvisioningStatusDto s) => s.Status == TenantProvisioningStatus.Completed;","tryCatchPattern":"try\n{\n    await tenantService.ActivateAsync(tenantId, ct);\n}\ncatch (CustomException ex) when (ex.Message.Contains(\"is not provisioned\"))\n{\n    var s = await provisioningService.GetStatusAsync(tenantId, ct);\n    logger.LogWarning(\"Activation blocked for {TenantId}, provisioning status: {Status}\", tenantId, s.Status);\n}","preventionTips":["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)."],"tags":["multitenancy","provisioning","activation","state"],"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"}