fullstackhero/dotnet-starter-kit · error · NotFoundException
Provisioning not found for tenant
Error message
Provisioning not found for tenant {tenantId}. What it means
GetStatusAsync loads the most recent TenantProvisioning record for the tenant and throws NotFoundException when none exists. It means provisioning has never been started for this tenant, so there is no status to report. The exception maps to a 404-style response.
Solutions
- Start provisioning first (StartAsync) before querying status.
- Verify the tenantId is correct and the tenant exists (Tenant {id} not found would otherwise surface).
- Check the API/DbMigrator is connected to the correct database/environment where provisioning ran.
- Handle the NotFound case in the client: treat it as 'never provisioned' and show a start-provisioning action instead of retrying.
Example fix
// before
var status = await provisioningService.GetStatusAsync(tenantId, ct);
// after
var tenant = await tenantService.GetAsync(tenantId, ct); // throws if tenant missing
var provisioning = await db.Set<TenantProvisioning>()
.FirstOrDefaultAsync(p => p.TenantId == tenantId, ct);
if (provisioning is null)
{
await provisioningService.StartAsync(tenantId, ct);
provisioning = await provisioningService.GetStatusAsync(tenantId, ct);
} Defensive patterns
Strategy: validation
Validate before calling
var provisioningExists = await db.Set<TenantProvisioning>().AnyAsync(p => p.TenantId == tenantId, ct); if (!provisioningExists) await provisioningService.StartAsync(tenantId, ct);
Type guard
bool HasProvisioning(TenantProvisioningStatusDto? s) => s is not null;
Try / catch
try
{
var status = await provisioningService.GetStatusAsync(tenantId, ct);
}
catch (NotFoundException)
{
// never provisioned — start it instead of polling forever
await provisioningService.StartAsync(tenantId, ct);
} Prevention
- Kick off provisioning immediately after tenant creation so status always exists.
- Validate tenant ids against the tenant list before querying status.
- Confirm environment/database alignment between where you provision and where you query.
- Handle the never-provisioned case explicitly in dashboards.
When it happens
Trigger: Calling GetStatusAsync for a tenantId that never had StartAsync invoked; passing a misspelled or stale tenant id; querying status after the provisioning rows were purged; checking status on the wrong environment/database.
Common situations: Dashboard polling status immediately after tenant creation but before provisioning starts; typo'd tenant id in a script; pointing the API at a different database than where provisioning ran; data cleanup job removed old provisioning records.
Understand the failure class
Background: Record Not Found Errors: "not found", RecordNotFound, and "was not found" — what they mean and how to fix them — this error's family across 28 libraries.
Related errors
- Tenant not found during provisioning.
- Tenant not found for provisioning.
- Provisioning for tenant not found.
- Invoice not found.
- Invoice not found.
AI-assisted analysis of fullstackhero/dotnet-starter-kit@3f2959e683 (2026-09-15).
Data as JSON: /api/errors/1e780cd044b78a47.
Report an issue: GitHub.
Appendix: source
Thrown at src/Modules/Multitenancy/Modules.Multitenancy/Provisioning/TenantProvisioningService.cs:88
await _dbContext.SaveChangesAsync(cancellationToken).ConfigureAwait(false);
return provisioning;
}
public async Task<TenantProvisioning?> GetLatestAsync(string tenantId, CancellationToken cancellationToken)
{
return await _dbContext.Set<TenantProvisioning>()
.Include(p => p.Steps)
.Where(p => p.TenantId == tenantId)
.OrderByDescending(p => p.CreatedUtc)
.FirstOrDefaultAsync(cancellationToken)
.ConfigureAwait(false);
}
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}.");
}
}
View on GitHub (pinned to 3f2959e683)