{"record":{"id":"0ebed856e72b97bc","repo":"fullstackhero/dotnet-starter-kit","slug":"channel-not-found-archivechannelcommandhandler","errorCode":null,"errorMessage":"Channel not found.","messagePattern":"Channel not found\\.","errorType":"exception","errorClass":"NotFoundException","httpStatus":404,"severity":"error","filePath":"src/Modules/Chat/Modules.Chat/Features/v1/Channels/ArchiveChannel/ArchiveChannelCommandHandler.cs","lineNumber":24,"sourceCode":"using Mediator;\nusing Microsoft.EntityFrameworkCore;\n\nnamespace FSH.Modules.Chat.Features.v1.Channels.ArchiveChannel;\n\npublic sealed class ArchiveChannelCommandHandler(\n    ChatDbContext db,\n    ICurrentUser currentUser)\n    : ICommandHandler<ArchiveChannelCommand, Unit>\n{\n    public async ValueTask<Unit> Handle(ArchiveChannelCommand cmd, CancellationToken cancellationToken)\n    {\n        ArgumentNullException.ThrowIfNull(cmd);\n        var userId = currentUser.GetUserId();\n        if (userId == Guid.Empty) throw new UnauthorizedException(\"no current user\");\n\n        var channel = await db.Channels.FirstOrDefaultAsync(c => c.Id == cmd.ChannelId, cancellationToken)\n            .ConfigureAwait(false)\n            ?? throw new NotFoundException(\"Channel not found.\");\n\n        channel.RequireAdmin(userId.ToString());\n\n        // Explicit soft-delete (not db.Remove): removing the aggregate cascades Deleted onto\n        // the ChannelMember rows, which the audit interceptor does not rescue (they're FK\n        // children, not owned), so they'd be hard-deleted and lost on restore.\n        channel.Archive(userId.ToString());\n        await db.SaveChangesAsync(cancellationToken).ConfigureAwait(false);\n        return Unit.Value;\n    }\n}\n","sourceCodeStart":6,"sourceCodeEnd":36,"githubUrl":"https://github.com/fullstackhero/dotnet-starter-kit/blob/3f2959e683e9f83f13e55e1678c9119f63c7e8e5/src/Modules/Chat/Modules.Chat/Features/v1/Channels/ArchiveChannel/ArchiveChannelCommandHandler.cs#L6-L36","documentation":"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.","triggerScenarios":"Archiving a ChannelId that doesn't exist; re-archiving an already-archived channel; stale client cache holding a deleted channel's ID.","commonSituations":"Double-click on the archive button; another admin archived first; environment mismatch (testing ID against production DB).","solutions":["Confirm the channel exists and is not already archived before calling","Refresh channel state from the server; treat 404 as 'already gone' and update UI","Deduplicate/serialize archive requests (disable button while pending)"],"exampleFix":"// before\nawait archiveChannel(channelId); // may 404 if already archived\n// after\nif (!channel.isArchived) {\n  await archiveChannel(channelId);\n}","handlingStrategy":"validation","validationCode":"var channel = await db.Channels.FirstOrDefaultAsync(c => c.Id == cmd.ChannelId);\nif (channel is null || channel.IsArchived) skip/404;","typeGuard":"bool archivable = channel is { } c && !c.IsArchived;","tryCatchPattern":"try { await archiveChannel(channelId); }\ncatch (NotFoundException) { /* already archived/gone — sync UI */ }","preventionTips":["Refresh channel state before archiving","Disable archive button while pending to avoid double-submit","Treat 404 as idempotent success for archive flows"],"tags":["chat","not-found","channel","archive"],"backgroundTag":"resource-not-found","analyzedSha":"3f2959e683e9f83f13e55e1678c9119f63c7e8e5","analyzedAt":"2026-09-15T22:20:53.684Z","contentChangedAt":"2026-09-15T22:20:53.684Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}