fullstackhero/dotnet-starter-kit · error · NotFoundException
Channel not found.
Error message
Channel not found.
What it means
AddChannelMembersCommandHandler throws NotFoundException when no channel with cmd.ChannelId exists in db.Channels. The lookup uses FirstOrDefaultAsync and throws instead of returning null so the endpoint maps to 404.
Solutions
- Verify the ChannelId exists (and isn't archived) before inviting members
- Re-fetch the channel list instead of using a cached ID
- Log the incoming ChannelId and confirm it against the database
Example fix
// before await addMembers(channelIdFromCache, userIds); // after var channel = await getChannel(channelId); if (channel) await addMembers(channelId, userIds);
Defensive patterns
Strategy: validation
Validate before calling
var exists = await db.Channels.AnyAsync(c => c.Id == cmd.ChannelId); if (!exists) return 404; // before inviting
Type guard
bool channelExists = channel is not null;
Try / catch
try { await addMembers(channelId, userIds); }
catch (NotFoundException) { /* refresh channel list; remove stale entry */ } Prevention
- Re-fetch channels instead of caching IDs long-term
- Validate GUIDs client-side before submit
- Treat 404 as 'channel gone' and clear local cache
When it happens
Trigger: Posting AddChannelMembers with a ChannelId that was never created, was archived/deleted (soft-delete removes it from queries), or a GUID typo / channel from another environment.
Common situations: Client cached a channel ID after it was archived; copy-pasted ID from another tenant/database; frontend using an undefined/stale variable as the ID.
Understand the failure class
Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.
Related errors
AI-assisted analysis of fullstackhero/dotnet-starter-kit@3f2959e683 (2026-09-15).
Data as JSON: /api/errors/17913e9f0c88d8e6.
Report an issue: GitHub.
Appendix: source
Thrown at src/Modules/Chat/Modules.Chat/Features/v1/Channels/AddChannelMembers/AddChannelMembersCommandHandler.cs:29
namespace FSH.Modules.Chat.Features.v1.Channels.AddChannelMembers;
public sealed class AddChannelMembersCommandHandler(
ChatDbContext db,
ICurrentUser currentUser,
IHubContext<AppHub> hub)
: ICommandHandler<AddChannelMembersCommand, Unit>
{
public async ValueTask<Unit> Handle(AddChannelMembersCommand cmd, CancellationToken cancellationToken)
{
ArgumentNullException.ThrowIfNull(cmd);
var userId = currentUser.GetUserId();
if (userId == Guid.Empty) throw new UnauthorizedException("no current user");
var currentUserId = userId.ToString();
var channel = await db.Channels.FirstOrDefaultAsync(c => c.Id == cmd.ChannelId, cancellationToken)
.ConfigureAwait(false)
?? throw new NotFoundException("Channel not found.");
// Members can invite to public channels they belong to; private channels require Admin.
var caller = channel.RequireMember(currentUserId);
if (channel.IsPrivate && caller.Role != ChannelMemberRole.Admin)
{
throw new ForbiddenException("Only channel admins can add members to private channels.");
}
var newlyAdded = new List<string>();
foreach (var uid in cmd.UserIds.Distinct(StringComparer.Ordinal))
{
// Skip duplicates silently — endpoint is idempotent for already-members.
if (channel.Members.Any(m => string.Equals(m.UserId, uid, StringComparison.Ordinal))) continue;
channel.AddMember(uid, currentUserId);
newlyAdded.Add(uid);
}
await db.SaveChangesAsync(cancellationToken).ConfigureAwait(false);View on GitHub (pinned to 3f2959e683)