fullstackhero/dotnet-starter-kit · error · NotFoundException

user not found

Error message

user not found

What it means

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.

Solutions

  1. Confirm the userId exists via the get-user-by-id endpoint in the same tenant context.
  2. Check you are operating in the correct tenant — Finbuckle filters may hide cross-tenant users.
  3. Re-fetch fresh user lists instead of reusing cached IDs.
  4. Validate the id is a non-empty GUID string before calling.

Example fix

// before: assumes the id is valid
await userRoleService.AssignRolesAsync(userId, roles, ct);
// after: guard first
if (!Guid.TryParse(userId, out var uid) || uid == Guid.Empty)
    throw new ArgumentException("A valid userId is required.", nameof(userId));
Defensive patterns

Strategy: validation

Validate before calling

function isValidUserId(id) {
  return typeof id === "string" && /^[0-9a-fA-F-]{36}$/.test(id) && id !== "00000000-0000-0000-0000-000000000000";
}

Try / catch

try { await assignRoles(userId, roles); } catch (e) {
  if (e.status === 404 && /user not found/i.test(e.message)) { refreshUserList(); return; }
  throw e;
}

Prevention

When it happens

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

Common situations: See trigger scenarios.

Understand the failure class

Background: "User not found", "Invalid user", and "does not exist": what missing-user lookup errors mean across Rocket.Chat, LiteLLM, Phabricator, rustfs, and pnpm — this error's family across 10 libraries.

Related errors


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

Appendix: source

Thrown at src/Modules/Identity/Modules.Identity/Services/UserRoleService.cs:29

using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;

namespace FSH.Modules.Identity.Services;

internal sealed class UserRoleService(
    UserManager<FshUser> userManager,
    RoleManager<FshRole> roleManager,
    IdentityDbContext db,
    IMultiTenantContextAccessor<AppTenantInfo> multiTenantContextAccessor,
    ICurrentUser currentUser,
    IUserPermissionService userPermissionService) : IUserRoleService
{
    public async Task<string> AssignRolesAsync(string userId, List<UserRoleDto> userRoles, CancellationToken cancellationToken)
    {
        var user = await userManager.Users
            .Where(u => u.Id == userId)
            .FirstOrDefaultAsync(cancellationToken)
            ?? throw new NotFoundException("user not found");

        await ValidateAdminRoleChangeAsync(user, userRoles);

        var assignedRoles = await ProcessRoleAssignmentsAsync(user, userRoles);

        await RaiseRolesAssignedEventAsync(user, assignedRoles, cancellationToken);

        // Any role mutation (add or remove) invalidates the cached permission set; flush
        // unconditionally rather than gating on assignedRoles, which only tracks additions.
        await userPermissionService.InvalidatePermissionCacheAsync(userId, cancellationToken).ConfigureAwait(false);

        return "User Roles Updated Successfully.";
    }

    public async Task<List<UserRoleDto>> GetUserRolesAsync(string userId, CancellationToken cancellationToken)
    {
        var user = await userManager.FindByIdAsync(userId)
            ?? throw new NotFoundException("user not found");

View on GitHub (pinned to 3f2959e683)