{"record":{"id":"1cf75d124dfd28c4","repo":"fullstackhero/dotnet-starter-kit","slug":"channel-not-found-restorechannelcommandhandler","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/RestoreChannel/RestoreChannelCommandHandler.cs","lineNumber":20,"sourceCode":"using FSH.Modules.Chat.Contracts.v1.Commands;\nusing FSH.Modules.Chat.Data;\nusing Mediator;\nusing Microsoft.EntityFrameworkCore;\n\nnamespace FSH.Modules.Chat.Features.v1.Channels.RestoreChannel;\n\npublic sealed class RestoreChannelCommandHandler(ChatDbContext db)\n    : ICommandHandler<RestoreChannelCommand, Unit>\n{\n    public async ValueTask<Unit> Handle(RestoreChannelCommand cmd, CancellationToken cancellationToken)\n    {\n        ArgumentNullException.ThrowIfNull(cmd);\n\n        // IgnoreQueryFilters bypasses the SoftDelete filter so we can find an archived channel.\n        var channel = await db.Channels.IgnoreQueryFilters()\n            .FirstOrDefaultAsync(c => c.Id == cmd.ChannelId, cancellationToken)\n            .ConfigureAwait(false)\n            ?? throw new NotFoundException(\"Channel not found.\");\n\n        if (!channel.IsDeleted) return Unit.Value; // idempotent\n        channel.Restore();\n        await db.SaveChangesAsync(cancellationToken).ConfigureAwait(false);\n        return Unit.Value;\n    }\n}\n","sourceCodeStart":2,"sourceCodeEnd":28,"githubUrl":"https://github.com/fullstackhero/dotnet-starter-kit/blob/3f2959e683e9f83f13e55e1678c9119f63c7e8e5/src/Modules/Chat/Modules.Chat/Features/v1/Channels/RestoreChannel/RestoreChannelCommandHandler.cs#L2-L28","documentation":"RestoreChannelCommandHandler looks the channel up with IgnoreQueryFilters() specifically so an archived (soft-deleted) channel can be found, then throws NotFoundException only if no channel with that Id exists at all (including deleted). If the channel exists but isn't deleted, the handler returns idempotently without error.","triggerScenarios":"Restoring a channel id that never existed, was hard-deleted, or belongs to another tenant — even IgnoreQueryFilters can't find it; calling restore on an id mistyped or from a different environment's DB.","commonSituations":"Archive list out of sync with the DB (channel purged); cross-tenant restore attempt; id taken from an audit log of another environment; duplicate restore after a hard delete by cleanup jobs.","solutions":["Verify the id exists in the Channels table (including IsDeleted rows) in the current tenant's DB.","Note restore is idempotent — a 404 means the row is truly gone, not already restored.","Use the audit/soft-delete data to confirm the channel was soft- (not hard-) deleted.","Handle the 404 in the archive UI by removing the entry from the list."],"exampleFix":"// before\nawait api.post(`/channels/${id}/restore`); // assumes it's just archived\n// after\ntry {\n  await api.post(`/channels/${id}/restore`);\n} catch (e) {\n  if (isNotFound(e)) removeFromArchiveList(id); // channel no longer exists at all\n  else throw e;\n}","handlingStrategy":"try-catch","validationCode":"const archived = await fetchArchivedChannel(channelId); // must still exist (soft-deleted ok)\nif (!archived) throw new Error('Channel no longer exists');","typeGuard":null,"tryCatchPattern":"try { ... } catch (e) {\n  if (isNotFound(e)) { removeFromArchiveUi(channelId); notify('Channel no longer exists'); }\n  else throw e;\n}","preventionTips":["Distinguish soft-deleted (restorable) from hard-deleted (gone) channels in the archive UI.","Refresh the archive list on cleanup-job completion.","Never assume restore is the fix for a missing channel — 404 means the row is truly absent.","Verify tenant context before restoring ids from logs/backups."],"tags":["chat","not-found","restore","soft-delete"],"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"}