{"record":{"id":"f5f8fbd3078b68f6","repo":"fullstackhero/dotnet-starter-kit","slug":"typeof-apptenantinfo-name-id-not-found","errorCode":null,"errorMessage":"{typeof(AppTenantInfo).Name} {id} Not Found.","messagePattern":"(.+?) (.+?) Not Found\\.","errorType":"exception","errorClass":"NotFoundException","httpStatus":404,"severity":"error","filePath":"src/Modules/Multitenancy/Modules.Multitenancy/Services/TenantService.cs","lineNumber":250,"sourceCode":"        var previous = tenant.ValidUpto;\n        tenant.ValidUpto = normalized;\n\n        await _tenantStore.UpdateAsync(tenant).ConfigureAwait(false);\n        await RefreshTenantCacheAsync(tenant).ConfigureAwait(false);\n\n        if (_logger.IsEnabled(LogLevel.Information))\n        {\n            _logger.LogInformation(\n                \"[Multitenancy] operator adjusted tenant {TenantId} validity from {Previous:o} to {ValidUpto:o}\",\n                id, previous, normalized);\n        }\n\n        return normalized;\n    }\n\n    private async Task<AppTenantInfo> GetTenantInfoAsync(string id, CancellationToken cancellationToken = default) =>\n        await _tenantStore.GetAsync(id).ConfigureAwait(false)\n            ?? throw new NotFoundException($\"{typeof(AppTenantInfo).Name} {id} Not Found.\");\n\n    // Finbuckle resolves via the distributed-cache store first (60-min TTL) while the injected store only\n    // writes EF, so push the new state into the cache store too — otherwise flips lag until cache expiry.\n    private async Task RefreshTenantCacheAsync(AppTenantInfo tenant)\n    {\n        var cacheStore = _serviceProvider\n            .GetServices<IMultiTenantStore<AppTenantInfo>>()\n            .FirstOrDefault(s => s.GetType() == typeof(DistributedCacheStore<AppTenantInfo>));\n        if (cacheStore is not null)\n        {\n            await cacheStore.UpdateAsync(tenant).ConfigureAwait(false);\n        }\n    }\n}","sourceCodeStart":232,"sourceCodeEnd":264,"githubUrl":"https://github.com/fullstackhero/dotnet-starter-kit/blob/3f2959e683e9f83f13e55e1678c9119f63c7e8e5/src/Modules/Multitenancy/Modules.Multitenancy/Services/TenantService.cs#L232-L264","documentation":"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.","triggerScenarios":"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.","commonSituations":"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).","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."],"exampleFix":"// before\nawait tenantService.ActivateAsync(id, ct); // id may be stale\n\n// after\nvar tenants = await tenantService.GetAllAsync(ct);\nif (!tenants.Any(t => t.Id.Equals(id, StringComparison.OrdinalIgnoreCase)))\n{\n    // tenant gone — refresh list instead of activating\n    return;\n}\nawait tenantService.ActivateAsync(id, ct);","handlingStrategy":"try-catch","validationCode":"var exists = (await tenantService.GetAllAsync(ct))\n    .Any(t => t.Id.Equals(id, StringComparison.OrdinalIgnoreCase));\nif (!exists) throw new KeyNotFoundException($\"Tenant {id} does not exist in this environment.\");","typeGuard":"AppTenantInfo? FindTenant(IEnumerable<AppTenantInfo> tenants, string id) =>\n    tenants.FirstOrDefault(t => t.Id.Equals(id, StringComparison.OrdinalIgnoreCase));","tryCatchPattern":"try\n{\n    await tenantService.ActivateAsync(id, ct);\n}\ncatch (NotFoundException)\n{\n    logger.LogWarning(\"Tenant {TenantId} not found — refreshing local cache\", id);\n    await RefreshTenantListAsync(); // drop stale id\n}","preventionTips":["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."],"tags":["multitenancy","not-found","tenant"],"backgroundTag":"entity-not-found","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"}