{"record":{"id":"f3b4ef33dc67d3a5","repo":"fullstackhero/dotnet-starter-kit","slug":"roles-not-found","errorCode":null,"errorMessage":"roles not found","messagePattern":"roles not found","errorType":"exception","errorClass":"NotFoundException","httpStatus":404,"severity":"warning","filePath":"src/Modules/Identity/Modules.Identity/Services/UserRoleService.cs","lineNumber":50,"sourceCode":"\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\");\n\n        var roles = await roleManager.Roles.AsNoTracking().ToListAsync(cancellationToken)\n            ?? throw new NotFoundException(\"roles not found\");\n\n        // Single membership query instead of one IsInRoleAsync round-trip per role.\n        var memberships = await userManager.GetRolesAsync(user);\n        var membershipSet = new HashSet<string>(memberships, StringComparer.OrdinalIgnoreCase);\n\n        var userRoles = new List<UserRoleDto>();\n        foreach (var role in roles)\n        {\n            userRoles.Add(new UserRoleDto\n            {\n                RoleId = role.Id,\n                RoleName = role.Name,\n                Description = role.Description,\n                Enabled = membershipSet.Contains(role.Name!)\n            });\n        }\n\n        return userRoles;","sourceCodeStart":32,"sourceCodeEnd":68,"githubUrl":"https://github.com/fullstackhero/dotnet-starter-kit/blob/3f2959e683e9f83f13e55e1678c9119f63c7e8e5/src/Modules/Identity/Modules.Identity/Services/UserRoleService.cs#L32-L68","documentation":"Thrown by GetUserRolesAsync when the role table query returns null, which in practice can only happen if the roles query yields nothing to the null-coalescing check (an empty roles list should normally never be null). It signals that no roles are available in the current tenant/database.","triggerScenarios":"Calling GET user-roles in a freshly provisioned database where identity seeding (role creation) never ran, or against a tenant with no roles provisioned.","commonSituations":"Skipping the DbMigrator seed step (`--seed`) in a new environment; a migration applied without role data; pointing the API at an empty database.","solutions":["Run the seeder: `dotnet run --project src/Host/FSH.Starter.DbMigrator -- apply --seed`.","Verify the Roles table contains rows for the current tenant.","If seeding is intentionally skipped, treat an empty role list as valid and skip the null-coalescing throw.","Check connection strings to confirm you are on the expected database."],"exampleFix":"// before: treats empty role list as not found\nvar roles = await roleManager.Roles.AsNoTracking().ToListAsync(ct) ?? throw new NotFoundException(\"roles not found\");\n// after: empty list is valid\nvar roles = await roleManager.Roles.AsNoTracking().ToListAsync(ct);","handlingStrategy":"fallback","validationCode":"// confirm roles exist before expecting role data\nconst roleCount = await apiFetch(\"/api/v1/roles\").then(r => r.length);","typeGuard":null,"tryCatchPattern":"try { roles = await getUserRoles(userId); } catch (e) {\n  if (/roles not found/i.test(e.message)) return []; // degrade gracefully for seeded-later DBs\n  throw e;\n}","preventionTips":["Always run the DbMigrator with --seed in new environments.","Verify the Roles table is populated after provisioning.","Pin role seeding to deployment checks so empty role tables are detected early."],"tags":["identity","roles","seeding"],"backgroundTag":"empty-result-set","analyzedSha":"3f2959e683e9f83f13e55e1678c9119f63c7e8e5","analyzedAt":"2026-09-15T22:20:53.684Z","contentChangedAt":"2026-09-15T22:20:53.684Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}