{"record":{"id":"0874419b7cbda0fc","repo":"fullstackhero/dotnet-starter-kit","slug":"user-not-found-userroleservice","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/UserRoleService.cs","lineNumber":29,"sourceCode":"using Microsoft.AspNetCore.Identity;\nusing Microsoft.EntityFrameworkCore;\n\nnamespace FSH.Modules.Identity.Services;\n\ninternal sealed class UserRoleService(\n    UserManager<FshUser> userManager,\n    RoleManager<FshRole> roleManager,\n    IdentityDbContext db,\n    IMultiTenantContextAccessor<AppTenantInfo> multiTenantContextAccessor,\n    ICurrentUser currentUser,\n    IUserPermissionService userPermissionService) : IUserRoleService\n{\n    public async Task<string> AssignRolesAsync(string userId, List<UserRoleDto> userRoles, CancellationToken cancellationToken)\n    {\n        var user = await userManager.Users\n            .Where(u => u.Id == userId)\n            .FirstOrDefaultAsync(cancellationToken)\n            ?? throw new NotFoundException(\"user not found\");\n\n        await ValidateAdminRoleChangeAsync(user, userRoles);\n\n        var assignedRoles = await ProcessRoleAssignmentsAsync(user, userRoles);\n\n        await RaiseRolesAssignedEventAsync(user, assignedRoles, cancellationToken);\n\n        // Any role mutation (add or remove) invalidates the cached permission set; flush\n        // unconditionally rather than gating on assignedRoles, which only tracks additions.\n        await userPermissionService.InvalidatePermissionCacheAsync(userId, cancellationToken).ConfigureAwait(false);\n\n        return \"User Roles Updated Successfully.\";\n    }\n\n    public async Task<List<UserRoleDto>> GetUserRolesAsync(string userId, CancellationToken cancellationToken)\n    {\n        var user = await userManager.FindByIdAsync(userId)\n            ?? throw new NotFoundException(\"user not found\");","sourceCodeStart":11,"sourceCodeEnd":47,"githubUrl":"https://github.com/fullstackhero/dotnet-starter-kit/blob/3f2959e683e9f83f13e55e1678c9119f63c7e8e5/src/Modules/Identity/Modules.Identity/Services/UserRoleService.cs#L11-L47","documentation":"Generic sentinel thrown by UserRoleService.AssignRolesAsync when the UserManager lookup for the supplied userId yields no FshUser. It fires before any role mutation, meaning the caller attempted to assign roles to a nonexistent (or tenant-invisible) user account.","triggerScenarios":"POST/PUT role-assignment endpoints called with a userId that is null, malformed, deleted, or belongs to a different tenant (tenant query filters hide the row).","commonSituations":"See trigger scenarios.","solutions":["Confirm the userId exists via the get-user-by-id endpoint in the same tenant context.","Check you are operating in the correct tenant — Finbuckle filters may hide cross-tenant users.","Re-fetch fresh user lists instead of reusing cached IDs.","Validate the id is a non-empty GUID string before calling."],"exampleFix":"// before: assumes the id is valid\nawait userRoleService.AssignRolesAsync(userId, roles, ct);\n// after: guard first\nif (!Guid.TryParse(userId, out var uid) || uid == Guid.Empty)\n    throw new ArgumentException(\"A valid userId is required.\", nameof(userId));","handlingStrategy":"validation","validationCode":"function isValidUserId(id) {\n  return typeof id === \"string\" && /^[0-9a-fA-F-]{36}$/.test(id) && id !== \"00000000-0000-0000-0000-000000000000\";\n}","typeGuard":null,"tryCatchPattern":"try { await assignRoles(userId, roles); } catch (e) {\n  if (e.status === 404 && /user not found/i.test(e.message)) { refreshUserList(); return; }\n  throw e;\n}","preventionTips":["Refresh user lists after deletions; never reuse long-cached ids.","Operate within the correct tenant context — cross-tenant ids look like missing users.","Validate GUID format client-side before submission."],"tags":["identity","roles","not-found"],"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"}