fullstackhero/dotnet-starter-kit · error · NotFoundException

Message not found.

Error message

Message not found.

What it means

The message lookup by cmd.MessageId found no row, so the handler throws NotFoundException('Message not found.') (HTTP 404). Thrown at the first query, when the message id does not exist or is filtered out (e.g. other tenant).

Solutions

  1. Verify the message id exists in the current tenant before pinning
  2. Refresh the message list in the client and surface 404 as 'message removed'
  3. Check soft-delete state in the Messages table
  4. Confirm tenant resolution matches the message's tenant
Defensive patterns

Strategy: validation

Validate before calling

const msg = messages.find(m => m.id === messageId);
if (!msg) throw new Error(`message ${messageId} not in current list`);

Type guard

function isPinnable(m: Message | undefined): m is Message { return m !== undefined && !m.deleted; }

Try / catch

try { await pinMessage(messageId); }
catch (e) { if (isNotFound(e)) { removeMessageFromUi(messageId); toast('Message was removed'); return; } throw e; }

Prevention

When it happens

Trigger: Pinning a MessageId that does not exist, was deleted, or belongs to another tenant.

Common situations: Client pins from stale list state after the message was deleted, cross-tenant id, or a bad id passed by an automation script.

Understand the failure class

Background: "Not found" and "does not exist" errors: why "Task not found", "No such folder", and "Can't find" fire when a lookup comes back empty — this error's family across 14 libraries.

Related errors


AI-assisted analysis of fullstackhero/dotnet-starter-kit@3f2959e683 (2026-09-15). Data as JSON: /api/errors/eb1b44f2ed97a853. Report an issue: GitHub.

Appendix: source

Thrown at src/Modules/Chat/Modules.Chat/Features/v1/Messages/PinMessage/PinMessageCommandHandler.cs:28

namespace FSH.Modules.Chat.Features.v1.Messages.PinMessage;

public sealed class PinMessageCommandHandler(
    ChatDbContext db,
    ICurrentUser currentUser,
    IHubContext<AppHub> hub)
    : ICommandHandler<PinMessageCommand, Unit>
{
    public async ValueTask<Unit> Handle(PinMessageCommand 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 message = await db.Messages.FirstOrDefaultAsync(m => m.Id == cmd.MessageId, cancellationToken)
            .ConfigureAwait(false)
            ?? throw new NotFoundException("Message not found.");

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

        message.Pin(currentUserId);
        await db.SaveChangesAsync(cancellationToken).ConfigureAwait(false);

        await hub.Clients.Group($"channel:{channel.Id}")
            .SendAsync("ChatMessagePinned", message.ToDto(), cancellationToken)
            .ConfigureAwait(false);
        return Unit.Value;
    }
}

View on GitHub (pinned to 3f2959e683)