fullstackhero/dotnet-starter-kit · error · NotFoundException
Webhook subscription
Error message
Webhook subscription {command.Id} not found. What it means
DeleteWebhookSubscription's handler loads the subscription by command Id and throws NotFoundException when none matches, before calling Remove. The webhook module therefore rejects deletion of unknown, soft-deleted/filtered, or out-of-tenant subscriptions with a typed not-found (HTTP 404) instead of silently doing nothing.
Solutions
- Treat the 404 as success for idempotent delete UX (the desired end state — subscription gone — is already achieved).
- Refresh the subscription list before deleting to confirm it still exists.
- Check the tenant context matches where the subscription was created.
- Verify the GUID is copied from the correct environment's list endpoint.
Example fix
// before (client)
await apiFetch(`/webhooks/v1/subscriptions/${id}`, { method: 'DELETE' }); // 404 on double delete
// after
try {
await apiFetch(`/webhooks/v1/subscriptions/${id}`, { method: 'DELETE' });
} catch (e) {
if (e.status !== 404) throw e; // idempotent: 404 already deleted
} Defensive patterns
Strategy: try-catch
Validate before calling
const sub = await api.getSubscription(id); if (!sub) return; // already gone — delete goal achieved
Try / catch
try {
await deleteSubscription(id);
} catch (ApiError e) when (e.Status === 404) {
// idempotent success
removeFromLocalList(id);
} Prevention
- Treat delete as idempotent on the client: 404 means already deleted.
- Disable delete buttons after first click to avoid duplicate requests.
- Refresh lists after mutations so stale ids are not reused.
When it happens
Trigger: Calling DeleteWebhookSubscription with a subscription Id that was already deleted (second delete of the same id), an id from another tenant, or a fabricated/mistyped GUID.
Common situations: Double-clicking a delete button so the second request 404s; a client list that wasn't refreshed after another admin removed the subscription; copying an id from logs of a different environment (staging vs production).
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/2cbf6d1e1d9a8446.
Report an issue: GitHub.
Appendix: source
Thrown at src/Modules/Webhooks/Modules.Webhooks/Features/v1/DeleteWebhookSubscription/DeleteWebhookSubscriptionCommandHandler.cs:19
using FSH.Framework.Core.Exceptions;
using FSH.Modules.Webhooks.Contracts.v1.DeleteWebhookSubscription;
using FSH.Modules.Webhooks.Data;
using Mediator;
using Microsoft.EntityFrameworkCore;
namespace FSH.Modules.Webhooks.Features.v1.DeleteWebhookSubscription;
public sealed class DeleteWebhookSubscriptionCommandHandler(
WebhookDbContext dbContext) : ICommandHandler<DeleteWebhookSubscriptionCommand>
{
public async ValueTask<Unit> Handle(DeleteWebhookSubscriptionCommand command, CancellationToken cancellationToken)
{
ArgumentNullException.ThrowIfNull(command);
var subscription = await dbContext.Subscriptions
.FirstOrDefaultAsync(s => s.Id == command.Id, cancellationToken)
.ConfigureAwait(false)
?? throw new NotFoundException($"Webhook subscription {command.Id} not found.");
dbContext.Subscriptions.Remove(subscription);
await dbContext.SaveChangesAsync(cancellationToken).ConfigureAwait(false);
return Unit.Value;
}
}
View on GitHub (pinned to 3f2959e683)