Symptom
When the in-memory team-member list of a TeamComponent<T> instance contains two or more rows matching the predicate that RemoveUserFromTeam uses to find the row being removed, the click-handler throws System.InvalidOperationException: Sequence contains more than one matching element and the row stays in the UI. Every subsequent click on a different row in the same team also throws (the duplicates remain in _teamMembers), so once a team has duplicates the team-management page is effectively unusable for that team until either the page is reloaded or the duplicates are deleted from the database.
Where
Tharga.Team.Blazor.Features.Team.TeamComponent<T>.RemoveUserFromTeam (line numbers obscured by Razor compilation; the <RemoveUserFromTeam>d__35.MoveNext async state machine is the giveaway).
Stack
System.Linq.ThrowHelper.ThrowMoreThanOneMatchException
System.Linq.Enumerable.TryGetSingle
System.Linq.Enumerable.Single
Tharga.Team.Blazor.Features.Team.TeamComponent`1+<RemoveUserFromTeam>d__35.MoveNext
How we noticed
We monitor production exceptions for Quilt4Net.Server (consumer of Tharga.Team.Blazor 2.0.16). On v4.2.16.0 we saw 5 occurrences of this exception in a 24-hour window — all from the same call site, all from a single team that had a duplicate TeamMember row in our Mongo instance.
Root cause
RemoveUserFromTeam uses .Single(predicate) on the in-memory list. The list is loaded from the database, and the database can carry duplicate rows (in our case from a migration glitch — your repository layer doesn't prevent duplicate (TeamKey, UserKey) rows). One bad row in the database becomes a hard 500 in the UI.
Suggested fix
Same pattern we just landed downstream for our analogous UserService.GetUserAsync duplicate-Identity case:
.FirstOrDefault(predicate) instead of .Single(predicate).
- If
> 1 matches existed (compute once, log once), _logger.LogWarning(...) with the team key + user key so operators can find the bad rows for cleanup.
- Continue removing the picked row; the leftover duplicates can be cleaned up on the next page load (or, if you're feeling ambitious, the remove operation could opportunistically delete all matching rows so the duplicate is gone after one click).
Defence layer
If the upstream root cause is acceptable to leave (duplicates are operator-fixable in seconds), at minimum surface a friendly error in the UI instead of letting the exception propagate to the boundary — currently it bricks the whole team-management page until reload.
Notes
Author of Tharga.Team.Blazor and the consumer that hit this is the same person, so no upstream-vs-downstream confusion — just filing here for visibility / so it doesn't get lost in the prod-exception-cleanup writeup. Happy to PR the fix if you want; just didn't want to push to your repo unannounced.
Symptom
When the in-memory team-member list of a
TeamComponent<T>instance contains two or more rows matching the predicate thatRemoveUserFromTeamuses to find the row being removed, the click-handler throwsSystem.InvalidOperationException: Sequence contains more than one matching elementand the row stays in the UI. Every subsequent click on a different row in the same team also throws (the duplicates remain in_teamMembers), so once a team has duplicates the team-management page is effectively unusable for that team until either the page is reloaded or the duplicates are deleted from the database.Where
Tharga.Team.Blazor.Features.Team.TeamComponent<T>.RemoveUserFromTeam(line numbers obscured by Razor compilation; the<RemoveUserFromTeam>d__35.MoveNextasync state machine is the giveaway).Stack
How we noticed
We monitor production exceptions for
Quilt4Net.Server(consumer ofTharga.Team.Blazor2.0.16). On v4.2.16.0 we saw 5 occurrences of this exception in a 24-hour window — all from the same call site, all from a single team that had a duplicateTeamMemberrow in our Mongo instance.Root cause
RemoveUserFromTeamuses.Single(predicate)on the in-memory list. The list is loaded from the database, and the database can carry duplicate rows (in our case from a migration glitch — your repository layer doesn't prevent duplicate(TeamKey, UserKey)rows). One bad row in the database becomes a hard 500 in the UI.Suggested fix
Same pattern we just landed downstream for our analogous
UserService.GetUserAsyncduplicate-Identity case:.FirstOrDefault(predicate)instead of.Single(predicate).> 1matches existed (compute once, log once),_logger.LogWarning(...)with the team key + user key so operators can find the bad rows for cleanup.Defence layer
If the upstream root cause is acceptable to leave (duplicates are operator-fixable in seconds), at minimum surface a friendly error in the UI instead of letting the exception propagate to the boundary — currently it bricks the whole team-management page until reload.
Notes
Author of
Tharga.Team.Blazorand the consumer that hit this is the same person, so no upstream-vs-downstream confusion — just filing here for visibility / so it doesn't get lost in the prod-exception-cleanup writeup. Happy to PR the fix if you want; just didn't want to push to your repo unannounced.