fullstackhero/dotnet-starter-kit · error · NotFoundException
Roles not found
Error message
Roles not found: {string.Join(", ", invalidRoleIds)} What it means
ValidateRoleIdsAsync loads the IDs of roles matching the requested roleIds and throws NotFoundException listing every requested ID that does not exist. UpdateGroup therefore refuses partial role updates: all supplied RoleIds must resolve to real roles.
Solutions
- Remove or correct the invalid role IDs reported in the message before resending the command.
- Refresh the role list from the API and rebuild the RoleIds selection from current data.
- Create the missing roles first if they are genuinely required, then retry the group update.
Example fix
// before
var cmd = new UpdateGroupCommand { Id = groupId, RoleIds = ["role-1", "role-deleted"] };
// after
var roles = await roleApi.ListAsync();
var validIds = cmd.RoleIds.Where(id => roles.Any(r => r.Id == id)).ToList();
if (validIds.Count != cmd.RoleIds.Count)
showWarning("Some roles no longer exist and were removed.");
await mediator.Send(new UpdateGroupCommand { Id = groupId, RoleIds = validIds }); Defensive patterns
Strategy: validation
Validate before calling
var roles = await roleApi.ListAsync();
var roleIds = new HashSet<string>(selectedRoleIds);
if (!roleIds.IsSubsetOf(roles.Select(r => r.Id)))
{
showValidationError("Some selected roles no longer exist. Refresh and reselect.");
} Type guard
string[] validRoleIds(IEnumerable<string> requested, IEnumerable<Role> existing) =>
requested.Where(id => existing.Any(r => r.Id == id)).ToArray(); Try / catch
try
{
await mediator.Send(new UpdateGroupCommand { Id = id, RoleIds = roleIds });
}
catch (NotFoundException ex)
{
// message lists the missing role IDs
await refreshRoleList();
notify($"Roles not found — reselect: {ex.Message}");
} Prevention
- Load RoleIds selection options live from the roles API, not from a static cache.
- Listen for role-deleted events/queries invalidation and prune them from open forms.
- Never copy role IDs across tenants or environments.
When it happens
Trigger: Sending UpdateGroupCommand with RoleIds containing one or more IDs that do not exist in the Roles table (deleted roles, IDs from another tenant/environment, malformed GUID strings that parse but match nothing).
Common situations: Frontend caches role options and a role was deleted meanwhile; copying role assignments from a template created in another tenant; importing group configuration JSON from a different environment.
Understand the failure class
Background: Record Not Found Errors: "not found", RecordNotFound, and "was not found" — what they mean and how to fix them — this error's family across 28 libraries.
Related errors
- Group with ID ' ' not found.
- role not found
- user not found
- Group with ID ' ' not found.
- Group with name ' ' already exists.
AI-assisted analysis of fullstackhero/dotnet-starter-kit@3f2959e683 (2026-09-15).
Data as JSON: /api/errors/2958664438360976.
Report an issue: GitHub.
Appendix: source
Thrown at src/Modules/Identity/Modules.Identity/Features/v1/Groups/UpdateGroup/UpdateGroupCommandHandler.cs:101
}
}
private async Task ValidateRoleIdsAsync(IReadOnlyList<string>? roleIds, CancellationToken cancellationToken)
{
if (roleIds is not { Count: > 0 })
{
return;
}
var existingRoleIds = await _dbContext.Roles
.Where(r => roleIds.Contains(r.Id))
.Select(r => r.Id)
.ToListAsync(cancellationToken);
var invalidRoleIds = roleIds.Except(existingRoleIds).ToList();
if (invalidRoleIds.Count > 0)
{
throw new NotFoundException($"Roles not found: {string.Join(", ", invalidRoleIds)}");
}
}
private static HashSet<string> UpdateRoleAssignments(Group group, IReadOnlyList<string>? roleIds)
{
var currentRoleIds = group.GroupRoles.Select(gr => gr.RoleId).ToHashSet();
var newRoleIds = roleIds?.ToHashSet() ?? [];
var rolesToRemove = group.GroupRoles.Where(gr => !newRoleIds.Contains(gr.RoleId)).ToList();
foreach (var role in rolesToRemove)
{
group.GroupRoles.Remove(role);
}
foreach (var roleId in newRoleIds.Where(id => !currentRoleIds.Contains(id)))
{
group.GroupRoles.Add(GroupRole.Create(group.Id, roleId));
}View on GitHub (pinned to 3f2959e683)