fullstackhero/dotnet-starter-kit · error · NotFoundException

Channel not found.

Error message

Channel not found.

What it means

ArchiveChannelCommandHandler throws NotFoundException when no channel with cmd.ChannelId exists. Channels are soft-deleted on archive, and archived channels no longer resolve via db.Channels, so archiving (or re-archiving) a missing/archived channel yields 404.

Solutions

  1. Confirm the channel exists and is not already archived before calling
  2. Refresh channel state from the server; treat 404 as 'already gone' and update UI
  3. Deduplicate/serialize archive requests (disable button while pending)

Example fix

// before
await archiveChannel(channelId); // may 404 if already archived
// after
if (!channel.isArchived) {
  await archiveChannel(channelId);
}
Defensive patterns

Strategy: validation

Validate before calling

var channel = await db.Channels.FirstOrDefaultAsync(c => c.Id == cmd.ChannelId);
if (channel is null || channel.IsArchived) skip/404;

Type guard

bool archivable = channel is { } c && !c.IsArchived;

Try / catch

try { await archiveChannel(channelId); }
catch (NotFoundException) { /* already archived/gone — sync UI */ }

Prevention

When it happens

Trigger: Archiving a ChannelId that doesn't exist; re-archiving an already-archived channel; stale client cache holding a deleted channel's ID.

Common situations: Double-click on the archive button; another admin archived first; environment mismatch (testing ID against production DB).

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

Appendix: source

Thrown at src/Modules/Chat/Modules.Chat/Features/v1/Channels/ArchiveChannel/ArchiveChannelCommandHandler.cs:24

using Mediator;
using Microsoft.EntityFrameworkCore;

namespace FSH.Modules.Chat.Features.v1.Channels.ArchiveChannel;

public sealed class ArchiveChannelCommandHandler(
    ChatDbContext db,
    ICurrentUser currentUser)
    : ICommandHandler<ArchiveChannelCommand, Unit>
{
    public async ValueTask<Unit> Handle(ArchiveChannelCommand cmd, CancellationToken cancellationToken)
    {
        ArgumentNullException.ThrowIfNull(cmd);
        var userId = currentUser.GetUserId();
        if (userId == Guid.Empty) throw new UnauthorizedException("no current user");

        var channel = await db.Channels.FirstOrDefaultAsync(c => c.Id == cmd.ChannelId, cancellationToken)
            .ConfigureAwait(false)
            ?? throw new NotFoundException("Channel not found.");

        channel.RequireAdmin(userId.ToString());

        // Explicit soft-delete (not db.Remove): removing the aggregate cascades Deleted onto
        // the ChannelMember rows, which the audit interceptor does not rescue (they're FK
        // children, not owned), so they'd be hard-deleted and lost on restore.
        channel.Archive(userId.ToString());
        await db.SaveChangesAsync(cancellationToken).ConfigureAwait(false);
        return Unit.Value;
    }
}

View on GitHub (pinned to 3f2959e683)