fullstackhero/dotnet-starter-kit · error · NotFoundException
Not Found.
Error message
{typeof(AppTenantInfo).Name} {id} Not Found. What it means
GetTenantInfoAsync loads the tenant from ITenantStore (Finbuckle) and throws NotFoundException formatted as '{AppTenantInfo} {id} Not Found.' when the store returns null. It is the shared loader for Activate/Deactivate/Update flows, so any tenant-scoped operation on a nonexistent id surfaces this error.
Solutions
- Verify the tenant id exists (list tenants or check the Tenants table) before operating on it.
- Refresh stale references in the client after tenant deletion.
- Confirm the API connects to the intended database and that DbMigrator ran (--apply --seed) so seed tenants exist.
- Handle NotFoundException in callers to return a clean 404 rather than a 500.
Example fix
// before
await tenantService.ActivateAsync(id, ct); // id may be stale
// after
var tenants = await tenantService.GetAllAsync(ct);
if (!tenants.Any(t => t.Id.Equals(id, StringComparison.OrdinalIgnoreCase)))
{
// tenant gone — refresh list instead of activating
return;
}
await tenantService.ActivateAsync(id, ct); Defensive patterns
Strategy: try-catch
Validate before calling
var exists = (await tenantService.GetAllAsync(ct))
.Any(t => t.Id.Equals(id, StringComparison.OrdinalIgnoreCase));
if (!exists) throw new KeyNotFoundException($"Tenant {id} does not exist in this environment."); Type guard
AppTenantInfo? FindTenant(IEnumerable<AppTenantInfo> tenants, string id) =>
tenants.FirstOrDefault(t => t.Id.Equals(id, StringComparison.OrdinalIgnoreCase)); Try / catch
try
{
await tenantService.ActivateAsync(id, ct);
}
catch (NotFoundException)
{
logger.LogWarning("Tenant {TenantId} not found — refreshing local cache", id);
await RefreshTenantListAsync(); // drop stale id
} Prevention
- Validate tenant ids against the tenant list before tenant-scoped calls.
- Invalidate cached tenant references after deletes.
- Verify DbMigrator ran and you are pointed at the right database/environment.
- Map NotFound to a clean 404 in endpoint handlers instead of letting it bubble as 500.
When it happens
Trigger: Calling ActivateAsync/DeactivateAsync/UpdateAsync with a tenant id that doesn't exist; stale id after a tenant was deleted; id casing/format mismatch with the store key; querying the wrong database or environment; the distributed-cache store was flushed and the underlying EF row is missing.
Common situations: Frontend holding a cached tenant id from before a delete; automation scripts with typo'd ids; pointing DbMigrator/API at a fresh database that hasn't been seeded; tenants created in another environment (staging vs production).
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
AI-assisted analysis of fullstackhero/dotnet-starter-kit@3f2959e683 (2026-09-15).
Data as JSON: /api/errors/f5f8fbd3078b68f6.
Report an issue: GitHub.
Appendix: source
Thrown at src/Modules/Multitenancy/Modules.Multitenancy/Services/TenantService.cs:250
var previous = tenant.ValidUpto;
tenant.ValidUpto = normalized;
await _tenantStore.UpdateAsync(tenant).ConfigureAwait(false);
await RefreshTenantCacheAsync(tenant).ConfigureAwait(false);
if (_logger.IsEnabled(LogLevel.Information))
{
_logger.LogInformation(
"[Multitenancy] operator adjusted tenant {TenantId} validity from {Previous:o} to {ValidUpto:o}",
id, previous, normalized);
}
return normalized;
}
private async Task<AppTenantInfo> GetTenantInfoAsync(string id, CancellationToken cancellationToken = default) =>
await _tenantStore.GetAsync(id).ConfigureAwait(false)
?? throw new NotFoundException($"{typeof(AppTenantInfo).Name} {id} Not Found.");
// Finbuckle resolves via the distributed-cache store first (60-min TTL) while the injected store only
// writes EF, so push the new state into the cache store too — otherwise flips lag until cache expiry.
private async Task RefreshTenantCacheAsync(AppTenantInfo tenant)
{
var cacheStore = _serviceProvider
.GetServices<IMultiTenantStore<AppTenantInfo>>()
.FirstOrDefault(s => s.GetType() == typeof(DistributedCacheStore<AppTenantInfo>));
if (cacheStore is not null)
{
await cacheStore.UpdateAsync(tenant).ConfigureAwait(false);
}
}
}View on GitHub (pinned to 3f2959e683)