fullstackhero/dotnet-starter-kit · error · NotFoundException

Roles not found

Error message

Roles not found: {string.Join(", ", invalidRoleIds)}

What it means

Thrown during group creation when one or more role IDs supplied in command.RoleIds do not match any role in the identity store; the handler pre-fetches Id+Name for the requested IDs and this exception fires for the subset that resolved to nothing (invalidRoleIds). It signals bad client input (stale or nonexistent role references), not a server failure.

Solutions

  1. Refresh the role list from GET /roles in the same tenant and resubmit with valid ids
  2. Drop the invalid role ids from the payload and create the group without them
  3. Recreate deleted roles if they are still required

Example fix

// before
roleIds: staleRoleIds // may include deleted roles
// after
var roles = await api.GetRoles();
roleIds: staleRoleIds.Where(id => roles.Any(r => r.Id == id)).ToList();
Defensive patterns

Strategy: validation

Validate before calling

var roleIds = (await api.ListRoles()).Select(r => r.Id).ToHashSet();
var invalid = cmd.RoleIds.Where(id => !roleIds.Contains(id)).ToList();

Type guard

static bool RolesResolve(IEnumerable<string> ids, ISet<string> known) => ids.All(known.Contains);

Prevention

When it happens

Trigger: CreateGroup with RoleIds containing deleted role ids, roles from another tenant, or malformed/empty GUID strings.

Common situations: Role removed by an admin between UI load and submit; copying RoleIds from another environment; stale permission catalogs on the client.

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/ee492c8b28e9eede. Report an issue: GitHub.

Appendix: source

Thrown at src/Modules/Identity/Modules.Identity/Features/v1/Groups/CreateGroup/CreateGroupCommandHandler.cs:49

        if (nameExists)
        {
            throw new CustomException($"Group with name '{command.Name}' already exists.", (IEnumerable<string>?)null, System.Net.HttpStatusCode.Conflict);
        }

        // Validate role IDs exist — fetch Id+Name in a single query to avoid a second roundtrip later
        List<(string Id, string Name)> resolvedRoles = [];
        if (command.RoleIds is { Count: > 0 })
        {
            var rawRoles = await _dbContext.Roles
                .Where(r => command.RoleIds.Contains(r.Id))
                .Select(r => new { r.Id, r.Name })
                .ToListAsync(cancellationToken);
            resolvedRoles = rawRoles.Select(r => (r.Id, r.Name!)).ToList();

            var invalidRoleIds = command.RoleIds.Except(resolvedRoles.Select(r => r.Id)).ToList();
            if (invalidRoleIds.Count > 0)
            {
                throw new NotFoundException($"Roles not found: {string.Join(", ", invalidRoleIds)}");
            }
        }

        var group = Group.Create(
            name: command.Name,
            description: command.Description,
            isDefault: command.IsDefault,
            isSystemGroup: false,
            createdBy: _currentUser.GetUserId().ToString());

        // Add role assignments
        foreach (var role in resolvedRoles)
        {
            _dbContext.GroupRoles.Add(GroupRole.Create(group.Id, role.Item1));
        }

        _dbContext.Groups.Add(group);
        await _dbContext.SaveChangesAsync(cancellationToken);

View on GitHub (pinned to 3f2959e683)