fullstackhero/dotnet-starter-kit · error · NotFoundException
Tenant not found for provisioning.
Error message
Tenant {tenantId} not found for provisioning. What it means
TenantProvisioningService.StartAsync throws NotFoundException ("Tenant {tenantId} not found for provisioning.") when asked to start a provisioning run for a tenant id that does not exist in the tenant store. This is the synchronous entry point before a run row is created; the job-side equivalent is TenantProvisioningJob.RunAsync. A subsequent guard also rejects starts while a run is already Pending/Running.
Solutions
- Confirm the tenantId exists (GET the tenant) before starting provisioning.
- Recreate the tenant if it was deleted and provisioning is needed.
- Refresh the admin UI's tenant list to drop stale entries.
- Fix the request payload if the id was mistyped or truncated.
Example fix
// before
await provisioningService.StartAsync("acme-mispelled", ct);
// after
var tenant = await tenantStore.GetAsync("acme", ct) ?? throw new NotFoundException("Tenant acme missing");
await provisioningService.StartAsync(tenant.Id, ct); Defensive patterns
Strategy: validation
Validate before calling
var tenant = await tenantStore.GetAsync(tenantId, ct);
if (tenant is null) return Result.NotFound($"Tenant {tenantId} does not exist."); Try / catch
try { await provisioningService.StartAsync(tenantId, ct); }
catch (NotFoundException) { refreshTenantList(); showError("Tenant not found — it may have been deleted."); } Prevention
- Fetch tenant ids from a live lookup, not cached lists.
- Disable provisioning actions for tenants pending deletion.
- Validate tenantId format/existence in the endpoint validator.
- Refresh tenant pickers after any tenant lifecycle operation.
When it happens
Trigger: Calling the provisioning start API/endpoint with an unknown or deleted tenantId; a client racing tenant deletion with a provisioning request; a typo'd or truncated tenant id in the request.
Common situations: Admin UI caching a tenant list while the tenant was deleted elsewhere; retrying a failed request after the tenant was removed; integration tests hitting a cleaned database.
Understand the failure class
Background: "Not found" and "does not exist" errors: why "Task not found", "No such folder", and "Can't find" fire when a lookup comes back empty — this error's family across 14 libraries.
Related errors
- Tenant not found during provisioning.
- Provisioning not found for tenant
- 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/7ddfdd35681199da.
Report an issue: GitHub.
Appendix: source
Thrown at src/Modules/Multitenancy/Modules.Multitenancy/Provisioning/TenantProvisioningService.cs:39
public TenantProvisioningService(
TenantDbContext dbContext,
IMultiTenantStore<AppTenantInfo> tenantStore,
IJobService jobService,
IServiceScopeFactory scopeFactory,
ILogger<TenantProvisioningService> logger)
{
_dbContext = dbContext;
_tenantStore = tenantStore;
_jobService = jobService;
_scopeFactory = scopeFactory;
_logger = logger;
}
public async Task<TenantProvisioning> StartAsync(string tenantId, CancellationToken cancellationToken)
{
var tenant = await _tenantStore.GetAsync(tenantId).ConfigureAwait(false)
?? throw new NotFoundException($"Tenant {tenantId} not found for provisioning.");
var existing = await GetLatestAsync(tenantId, cancellationToken).ConfigureAwait(false);
if (existing is not null && (existing.Status is TenantProvisioningStatus.Running or TenantProvisioningStatus.Pending))
{
throw new CustomException($"Provisioning already running for tenant {tenantId}.");
}
var correlationId = Guid.NewGuid().ToString();
var provisioning = new TenantProvisioning(tenant.Id, correlationId);
provisioning.Steps.Add(new TenantProvisioningStep(provisioning.Id, TenantProvisioningStepName.Database));
provisioning.Steps.Add(new TenantProvisioningStep(provisioning.Id, TenantProvisioningStepName.Migrations));
provisioning.Steps.Add(new TenantProvisioningStep(provisioning.Id, TenantProvisioningStepName.Seeding));
provisioning.Steps.Add(new TenantProvisioningStep(provisioning.Id, TenantProvisioningStepName.CacheWarm));
_dbContext.Add(provisioning);
await _dbContext.SaveChangesAsync(cancellationToken).ConfigureAwait(false);
View on GitHub (pinned to 3f2959e683)