fullstackhero/dotnet-starter-kit · error · NotFoundException

User with ID ' ' not found.

Error message

User with ID '{query.UserId}' not found.

What it means

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.

Solutions

  1. Verify the userId exists: query Users by Id (with the correct tenant context) before calling the endpoint.
  2. Check the X-Tenant-Id / tenant resolution — the user may exist in a different tenant than the one the request resolves to.
  3. Ensure the client stores and sends the correct, current user id; refresh cached user lists after deletions.

Example fix

// before
const res = await apiFetch(`/api/v1/users/${staleId}/user-groups`);
// after
const users = await searchUsers(email);
if (!users.length) throw new Error(`User ${email} not found`);
const res = await apiFetch(`/api/v1/users/${users[0].id}/user-groups`);
Defensive patterns

Strategy: validation

Validate before calling

const userExists = await checkUserExists(userId, tenantId);
if (!userExists) throw new ApiError(404, `User ${userId} not found`);
// then call GET /users/{userId}/user-groups

Type guard

function hasValidUserId(id) {
  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);
}

Try / catch

try { return await apiFetch(`/users/${id}/user-groups`); }
catch (e) {
  if (e.status === 404) { refreshUserCache(); return []; }
  throw e;
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Understand the failure class

Background: "Not found" and "does not exist" errors: why "Task not found", "No such folder", and "Can't find" fire when a lookup comes back empty — this error's family across 14 libraries.

Related errors


AI-assisted analysis of fullstackhero/dotnet-starter-kit@3f2959e683 (2026-09-15). Data as JSON: /api/errors/f3941c9e2cc0e6a6. Report an issue: GitHub.

Appendix: source

Thrown at src/Modules/Identity/Modules.Identity/Features/v1/Users/GetUserGroups/GetUserGroupsQueryHandler.cs:28

public sealed class GetUserGroupsQueryHandler : IQueryHandler<GetUserGroupsQuery, IEnumerable<GroupDto>>
{
    private readonly IdentityDbContext _dbContext;

    public GetUserGroupsQueryHandler(IdentityDbContext dbContext)
    {
        _dbContext = dbContext;
    }

    public async ValueTask<IEnumerable<GroupDto>> Handle(GetUserGroupsQuery query, CancellationToken cancellationToken)
    {
        // Validate user exists
        var userExists = await _dbContext.Users
            .AsNoTracking()
            .AnyAsync(u => u.Id == query.UserId, cancellationToken);

        if (!userExists)
        {
            throw new NotFoundException($"User with ID '{query.UserId}' not found.");
        }

        // Get user's groups
        var groupIds = await _dbContext.UserGroups
            .AsNoTracking()
            .Where(ug => ug.UserId == query.UserId)
            .Select(ug => ug.GroupId)
            .ToListAsync(cancellationToken);

        if (groupIds.Count == 0)
        {
            return [];
        }

        var groups = await _dbContext.Groups
            .AsNoTracking()
            .Include(g => g.GroupRoles)
            .Where(g => groupIds.Contains(g.Id))

View on GitHub (pinned to 3f2959e683)