{"record":{"id":"f3941c9e2cc0e6a6","repo":"fullstackhero/dotnet-starter-kit","slug":"user-with-id-query-userid-not-found","errorCode":null,"errorMessage":"User with ID '{query.UserId}' not found.","messagePattern":"User with ID '(.+?)' not found\\.","errorType":"exception","errorClass":"NotFoundException","httpStatus":404,"severity":"error","filePath":"src/Modules/Identity/Modules.Identity/Features/v1/Users/GetUserGroups/GetUserGroupsQueryHandler.cs","lineNumber":28,"sourceCode":"public sealed class GetUserGroupsQueryHandler : IQueryHandler<GetUserGroupsQuery, IEnumerable<GroupDto>>\n{\n    private readonly IdentityDbContext _dbContext;\n\n    public GetUserGroupsQueryHandler(IdentityDbContext dbContext)\n    {\n        _dbContext = dbContext;\n    }\n\n    public async ValueTask<IEnumerable<GroupDto>> Handle(GetUserGroupsQuery query, CancellationToken cancellationToken)\n    {\n        // Validate user exists\n        var userExists = await _dbContext.Users\n            .AsNoTracking()\n            .AnyAsync(u => u.Id == query.UserId, cancellationToken);\n\n        if (!userExists)\n        {\n            throw new NotFoundException($\"User with ID '{query.UserId}' not found.\");\n        }\n\n        // Get user's groups\n        var groupIds = await _dbContext.UserGroups\n            .AsNoTracking()\n            .Where(ug => ug.UserId == query.UserId)\n            .Select(ug => ug.GroupId)\n            .ToListAsync(cancellationToken);\n\n        if (groupIds.Count == 0)\n        {\n            return [];\n        }\n\n        var groups = await _dbContext.Groups\n            .AsNoTracking()\n            .Include(g => g.GroupRoles)\n            .Where(g => groupIds.Contains(g.Id))","sourceCodeStart":10,"sourceCodeEnd":46,"githubUrl":"https://github.com/fullstackhero/dotnet-starter-kit/blob/3f2959e683e9f83f13e55e1678c9119f63c7e8e5/src/Modules/Identity/Modules.Identity/Features/v1/Users/GetUserGroups/GetUserGroupsQueryHandler.cs#L10-L46","documentation":"GetUserGroupsQueryHandler first checks that the requested user exists in the Users table; if no row matches query.UserId it throws NotFoundException with a message naming the ID. This distinguishes a bad user ID from a user that simply has no groups.","triggerScenarios":"GET user-groups with a userId that was deleted, belongs to another tenant (global query filters exclude it), is a malformed/never-existing Guid, or referencing a soft-deleted user.","commonSituations":"Stale links/bookmarks after user removal; cross-tenant data access where Finbuckle's tenant filter hides the row; frontend passing the admin's id instead of the target user's; importing IDs from a different database.","solutions":["Verify the userId exists: query Users by Id (with the correct tenant context) before calling the endpoint.","Check the X-Tenant-Id / tenant resolution — the user may exist in a different tenant than the one the request resolves to.","Ensure the client stores and sends the correct, current user id; refresh cached user lists after deletions."],"exampleFix":"// before\nconst res = await apiFetch(`/api/v1/users/${staleId}/user-groups`);\n// after\nconst users = await searchUsers(email);\nif (!users.length) throw new Error(`User ${email} not found`);\nconst res = await apiFetch(`/api/v1/users/${users[0].id}/user-groups`);","handlingStrategy":"validation","validationCode":"const userExists = await checkUserExists(userId, tenantId);\nif (!userExists) throw new ApiError(404, `User ${userId} not found`);\n// then call GET /users/{userId}/user-groups","typeGuard":"function hasValidUserId(id) {\n  return typeof id === 'string' && /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(id);\n}","tryCatchPattern":"try { return await apiFetch(`/users/${id}/user-groups`); }\ncatch (e) {\n  if (e.status === 404) { refreshUserCache(); return []; }\n  throw e;\n}","preventionTips":["Refresh cached user lists after delete operations.","Always send the tenant header matching the user's tenant.","Validate Guid format client-side before calling.","Treat 404 on user-scoped endpoints as possible cross-tenant isolation, not just missing data."],"tags":["identity","not-found","users"],"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"}