fullstackhero/dotnet-starter-kit · error · NotFoundException
User ' ' is not a member of group ' '.
Error message
User '{command.UserId}' is not a member of group '{command.GroupId}'. What it means
RemoveUserFromGroup looks up the UserGroups membership row for the (GroupId, UserId) pair and throws NotFoundException if none exists — the 404 message names both ids. It distinguishes 'not a member' from 'group missing' by including both ids in the text.
Solutions
- Treat 404 as success in idempotent removal flows (desired end state already reached)
- Check membership via GET /groups/{groupId}/members before removing
- Verify you didn't swap the groupId and userId path segments
Example fix
// before
await api.RemoveUserFromGroup(groupId, userId); // throws on retry
// after
try { await api.RemoveUserFromGroup(groupId, userId); }
catch (ApiException e) when (e.Status == 404) { /* already removed */ } Defensive patterns
Strategy: try-catch
Validate before calling
const members = await api.GetGroupMembers(groupId); if (!members.some(m => m.userId === userId)) return; // nothing to remove
Try / catch
try { await api.RemoveUserFromGroup(groupId, userId); }
catch (ApiException e) when (e.Status == 404) { /* idempotent: not a member */ } Prevention
- Design remove-membership as idempotent (404 = already done)
- Verify URL parameter order (groupId then userId)
- Check membership before remove in UI flows
When it happens
Trigger: DELETE /groups/{groupId}/users/{userId} where the user was never added, already removed (double delete), or either id is cross-tenant/invalid.
Common situations: Idempotent retry of a remove that already succeeded; removing a user whose membership row was cascaded away when the group or user was deleted; swapped groupId/userId in the URL.
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
- Group with ID ' ' not found.
- Group with ID ' ' not found.
- Group with ID ' ' not found.
- Group with ID ' ' not found.
- Users not found
AI-assisted analysis of fullstackhero/dotnet-starter-kit@3f2959e683 (2026-09-15).
Data as JSON: /api/errors/ed22b153710bc553.
Report an issue: GitHub.
Appendix: source
Thrown at src/Modules/Identity/Modules.Identity/Features/v1/Groups/RemoveUserFromGroup/RemoveUserFromGroupCommandHandler.cs:31
private readonly IUserPermissionService _userPermissionService;
public RemoveUserFromGroupCommandHandler(IdentityDbContext dbContext, IUserPermissionService userPermissionService)
{
_dbContext = dbContext;
_userPermissionService = userPermissionService;
}
public async ValueTask<Unit> Handle(RemoveUserFromGroupCommand command, CancellationToken cancellationToken)
{
ArgumentNullException.ThrowIfNull(command);
var membership = await _dbContext.UserGroups
.Include(ug => ug.Group)
.FirstOrDefaultAsync(ug => ug.GroupId == command.GroupId && ug.UserId == command.UserId, cancellationToken);
if (membership is null)
{
throw new NotFoundException($"User '{command.UserId}' is not a member of group '{command.GroupId}'.");
}
// Default groups (e.g. seeded "All Users") require every tenant user to be a member, so
// removing one breaks that invariant and leaves later registrants in a half-populated group.
if (membership.Group is not null && membership.Group.IsDefault)
{
throw new ForbiddenException("Users cannot be removed from a default group.");
}
_dbContext.UserGroups.Remove(membership);
await _dbContext.SaveChangesAsync(cancellationToken);
// Leaving a group may revoke roles the user only held through this group —
// invalidate so the cached permission set is rebuilt on next request.
await _userPermissionService.InvalidatePermissionCacheAsync(command.UserId, cancellationToken).ConfigureAwait(false);
return Unit.Value;
}View on GitHub (pinned to 3f2959e683)