fullstackhero/dotnet-starter-kit · error · NotFoundException
Ticket not found.
Error message
Ticket {command.TicketId} not found. What it means
UpdateTicket's handler fetches the ticket by TicketId with default (soft-delete + tenant) query filters and throws NotFoundException if no row matches. The handler refuses to update a nonexistent, soft-deleted, or out-of-tenant ticket, mapping the failure to the app's typed 404 exception.
Solutions
- Re-fetch the ticket list to confirm the ticket still exists and is not deleted before updating.
- If soft-deleted, call RestoreTicket first, then retry UpdateTicket.
- Verify the tenant context (header/subdomain) matches the ticket's tenant.
- Handle the 404 in the client (refresh form state) instead of blindly retrying.
Example fix
// before
await mutateUpdate({ id: staleId, title, description, priority }); // 404
// after
const res = await getTicket(staleId);
if (!res) { refreshList(); notify('Ticket no longer exists'); return; }
await mutateUpdate({ id: staleId, title, description, priority }); Defensive patterns
Strategy: try-catch
Validate before calling
const ticket = await api.getTicket(id);
if (!ticket || ticket.deleted) { refreshForm(); return; } Try / catch
try {
await updateTicket({ id, title, description, priority });
} catch (ApiError e) when (e.Status === 404) {
resetEditForm();
notify('Ticket was deleted by another user');
} Prevention
- Refresh form data immediately before submitting edits.
- Use optimistic-concurrency UI (reload on conflict/404).
- Keep tenant headers consistent across list and update calls.
When it happens
Trigger: Submitting UpdateTicket (title/description/priority changes) for a TicketId that doesn't exist, was soft-deleted, or lives in another tenant; a stale UI form holding an id of a ticket deleted concurrently by another user.
Common situations: Two operators editing the same ticket list, one deletes while the other saves; admin app and dashboard app pointing at different tenants with a copied link; automated scripts replaying updates for tickets purged in the meantime.
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/f59e61faa2efce23.
Report an issue: GitHub.
Appendix: source
Thrown at src/Modules/Tickets/Modules.Tickets/Features/v1/Tickets/UpdateTicket/UpdateTicketCommandHandler.cs:19
using FSH.Framework.Core.Exceptions;
using FSH.Modules.Tickets.Contracts.v1.Tickets;
using FSH.Modules.Tickets.Data;
using Mediator;
using Microsoft.EntityFrameworkCore;
namespace FSH.Modules.Tickets.Features.v1.Tickets.UpdateTicket;
public sealed class UpdateTicketCommandHandler(TicketsDbContext dbContext)
: ICommandHandler<UpdateTicketCommand, Guid>
{
public async ValueTask<Guid> Handle(UpdateTicketCommand command, CancellationToken cancellationToken)
{
ArgumentNullException.ThrowIfNull(command);
var ticket = await dbContext.Tickets
.FirstOrDefaultAsync(t => t.Id == command.TicketId, cancellationToken)
.ConfigureAwait(false)
?? throw new NotFoundException($"Ticket {command.TicketId} not found.");
ticket.UpdateDetails(command.Title, command.Description, command.Priority);
await dbContext.SaveChangesAsync(cancellationToken).ConfigureAwait(false);
return ticket.Id;
}
}
View on GitHub (pinned to 3f2959e683)