{"record":{"id":"3c73bda2f52b84be","repo":"fullstackhero/dotnet-starter-kit","slug":"user-not-found-userprofileservice","errorCode":null,"errorMessage":"user not found","messagePattern":"user not found","errorType":"exception","errorClass":"NotFoundException","httpStatus":404,"severity":"error","filePath":"src/Modules/Identity/Modules.Identity/Services/UserProfileService.cs","lineNumber":37,"sourceCode":"    UserManager<FshUser> userManager,\n    SignInManager<FshUser> signInManager,\n    IStorageService storageService,\n    IMultiTenantContextAccessor<AppTenantInfo> multiTenantContextAccessor,\n    IOptions<OriginOptions> originOptions,\n    IHttpContextAccessor httpContextAccessor) : IUserProfileService\n{\n    private readonly Uri? _originUrl = originOptions.Value.OriginUrl;\n\n    public async Task<UserDto> GetAsync(string userId, CancellationToken cancellationToken)\n    {\n        // Relies on Finbuckle's tenant filter — callers can only ever read\n        // their own user record, which is in the request's resolved tenant.\n        var user = await userManager.Users\n            .AsNoTracking()\n            .Where(u => u.Id == userId)\n            .FirstOrDefaultAsync(cancellationToken);\n\n        _ = user ?? throw new NotFoundException(\"user not found\");\n\n        return new UserDto\n        {\n            Id = user.Id,\n            Email = user.Email,\n            UserName = user.UserName,\n            FirstName = user.FirstName,\n            LastName = user.LastName,\n            ImageUrl = ResolveImageUrl(user.ImageUrl),\n            IsActive = user.IsActive,\n            EmailConfirmed = user.EmailConfirmed,\n            PhoneNumber = user.PhoneNumber,\n            TwoFactorEnabled = user.TwoFactorEnabled,\n        };\n    }\n\n    public Task<int> GetCountAsync(CancellationToken cancellationToken) =>\n        userManager.Users.AsNoTracking().CountAsync(cancellationToken);","sourceCodeStart":19,"sourceCodeEnd":55,"githubUrl":"https://github.com/fullstackhero/dotnet-starter-kit/blob/3f2959e683e9f83f13e55e1678c9119f63c7e8e5/src/Modules/Identity/Modules.Identity/Services/UserProfileService.cs#L19-L55","documentation":"GetAsync throws NotFoundException('user not found') when no user with the given userId exists in the request's resolved tenant (query over userManager.Users finds nothing). It is a read-path 404: the profile endpoint was called with an id that doesn't match any row in the current tenant's user table.","triggerScenarios":"GET profile with a userId that doesn't exist, was deleted, or belongs to a different tenant (tenant filter hides it).","commonSituations":"Client caching stale user ids after a DB re-seed; cross-tenant lookup attempts; typos or id mismatches in the caller's data; tenant context resolving to the wrong tenant so the row is filtered out.","solutions":["Confirm the userId exists in the current tenant (query AspNetUsers directly).","Check tenant resolution — the user may live in a different tenant than the request resolved to.","Refresh any client-side cached user ids after database resets or migrations.","Fix the caller to use the authenticated user's own id (the endpoint reads the current user's record)."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"const res = await fetch(`/api/users/${id}/exists`);\nif (!res.ok) return null; // avoid calling GetAsync for a non-existent id","typeGuard":"function isUserDto(x): x is UserDto { return !!x && typeof x === 'object' && typeof (x as any).id === 'string'; }","tryCatchPattern":"try { const user = await getProfile(userId); }\ncatch (e) { if (e.status === 404) { showNotFound(userId); } else { throw e; } }","preventionTips":["Use the authenticated principal's id instead of client-stored ids where possible.","Invalidate cached user references after database resets.","Confirm tenant headers are sent on every profile call.","Handle 404 explicitly in profile UIs — it is an expected outcome."],"tags":["identity","not-found","multitenancy"],"backgroundTag":"user-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"}