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
- Verify the message id exists in the current tenant before pinning
- Refresh the message list in the client and surface 404 as 'message removed'
- Check soft-delete state in the Messages table
- 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
- Sync message list after deletes
- Only pin ids from live server state
- Match tenant context with the message tenant
- Treat 404 as 'message gone' UI state
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
- Message not found.
- Message not found.
- Parent message not found.
- User is not a member.
- Channel not found.
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)