{"record":{"id":"9587ef9ada292bd4","repo":"fullstackhero/dotnet-starter-kit","slug":"parent-message-not-found","errorCode":null,"errorMessage":"Parent message not found.","messagePattern":"Parent message not found\\.","errorType":"exception","errorClass":"NotFoundException","httpStatus":404,"severity":"error","filePath":"src/Modules/Chat/Modules.Chat/Features/v1/Messages/ListMessageReplies/ListMessageRepliesQueryHandler.cs","lineNumber":34,"sourceCode":"    IMediator mediator)\n    : IQueryHandler<ListMessageRepliesQuery, ReadOnlyCollection<MessageDto>>\n{\n    public async ValueTask<ReadOnlyCollection<MessageDto>> Handle(\n        ListMessageRepliesQuery query,\n        CancellationToken cancellationToken)\n    {\n        ArgumentNullException.ThrowIfNull(query);\n        var userId = currentUser.GetUserId();\n        if (userId == Guid.Empty) throw new UnauthorizedException(\"no current user\");\n        var currentUserId = userId.ToString();\n\n        // Load the parent so we can authorize the caller through the channel.\n        var parent = await db.Messages\n            .Where(m => m.Id == query.ParentMessageId)\n            .Select(m => new { m.Id, m.ChannelId })\n            .FirstOrDefaultAsync(cancellationToken)\n            .ConfigureAwait(false)\n            ?? throw new NotFoundException(\"Parent message not found.\");\n\n        var channel = await db.Channels.FirstOrDefaultAsync(c => c.Id == parent.ChannelId, cancellationToken)\n            .ConfigureAwait(false)\n            ?? throw new NotFoundException(\"Parent message not found.\");\n        channel.RequireMember(currentUserId);\n\n        IQueryable<Domain.Message> q = db.Messages\n            .Where(m => m.ParentMessageId == query.ParentMessageId);\n\n        if (query.Before is { } beforeId)\n        {\n            q = q.Where(m => m.Id.CompareTo(beforeId) < 0);\n        }\n\n        var rows = await q\n            .OrderByDescending(m => m.Id)\n            .Take(query.PageSize)\n            .Include(m => m.Attachments)","sourceCodeStart":16,"sourceCodeEnd":52,"githubUrl":"https://github.com/fullstackhero/dotnet-starter-kit/blob/3f2959e683e9f83f13e55e1678c9119f63c7e8e5/src/Modules/Chat/Modules.Chat/Features/v1/Messages/ListMessageReplies/ListMessageRepliesQueryHandler.cs#L16-L52","documentation":"The parent message lookup by query.ParentMessageId found no row, so the handler throws NotFoundException('Parent message not found.') (HTTP 404). Thrown at the parent query itself when the message id simply doesn't exist (or is invisible due to tenant filtering).","triggerScenarios":"Listing replies for a ParentMessageId that does not exist, was hard/soft deleted, or belongs to another tenant.","commonSituations":"Client holds a reply-to id from a deleted message, cross-tenant id, or stale UI state pointing at a purged thread.","solutions":["Verify the parent message id exists in the current tenant before querying replies","Refresh thread state on the client; handle 404 by removing the stale parent from the UI","Confirm tenant resolution matches the message's tenant","Check DB for soft-deleted message rows if the id should exist"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"if (!parentMessageId) throw new Error('parentMessageId is required');\nif (!messages.some(m => m.id === parentMessageId)) throw new Error('parent message not loaded');","typeGuard":"function hasParent(m: Message | undefined): m is Message { return m !== undefined && !m.deleted; }","tryCatchPattern":"try { return await listMessageReplies(parentId); }\ncatch (e) { if (isNotFound(e)) { removeThreadFromUi(parentId); return []; } throw e; }","preventionTips":["Remove threads from UI when parent is deleted","Validate ids come from fresh server state, not stale caches","Keep tenant context consistent across navigations","Handle 404 as 'thread gone', not an app error"],"tags":["chat","not-found","message"],"backgroundTag":"entity-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"}