{"record":{"id":"67113e1f55bf50d5","repo":"fullstackhero/dotnet-starter-kit","slug":"category-command-categoryid-not-found","errorCode":null,"errorMessage":"Category {command.CategoryId} not found.","messagePattern":"Category (.+?) not found\\.","errorType":"exception","errorClass":"NotFoundException","httpStatus":404,"severity":"error","filePath":"src/Modules/Catalog/Modules.Catalog/Features/v1/Categories/DeleteCategory/DeleteCategoryCommandHandler.cs","lineNumber":20,"sourceCode":"using FSH.Framework.Core.Exceptions;\nusing FSH.Modules.Catalog.Contracts.v1.Categories;\nusing FSH.Modules.Catalog.Data;\nusing Mediator;\nusing Microsoft.EntityFrameworkCore;\n\nnamespace FSH.Modules.Catalog.Features.v1.Categories.DeleteCategory;\n\npublic sealed class DeleteCategoryCommandHandler(CatalogDbContext dbContext)\n    : ICommandHandler<DeleteCategoryCommand, Unit>\n{\n    public async ValueTask<Unit> Handle(DeleteCategoryCommand command, CancellationToken cancellationToken)\n    {\n        ArgumentNullException.ThrowIfNull(command);\n\n        var category = await dbContext.Categories\n            .FirstOrDefaultAsync(c => c.Id == command.CategoryId, cancellationToken)\n            .ConfigureAwait(false)\n            ?? throw new NotFoundException($\"Category {command.CategoryId} not found.\");\n\n        bool hasChildren = await dbContext.Categories\n            .AnyAsync(c => c.ParentCategoryId == category.Id, cancellationToken)\n            .ConfigureAwait(false);\n        if (hasChildren)\n        {\n            throw new CustomException(\n                \"Cannot delete a category that has child categories. Move or remove the children first.\",\n                (IEnumerable<string>?)null,\n                HttpStatusCode.Conflict);\n        }\n\n        dbContext.Categories.Remove(category);\n        await dbContext.SaveChangesAsync(cancellationToken).ConfigureAwait(false);\n        return Unit.Value;\n    }\n}\n","sourceCodeStart":2,"sourceCodeEnd":38,"githubUrl":"https://github.com/fullstackhero/dotnet-starter-kit/blob/3f2959e683e9f83f13e55e1678c9119f63c7e8e5/src/Modules/Catalog/Modules.Catalog/Features/v1/Categories/DeleteCategory/DeleteCategoryCommandHandler.cs#L2-L38","documentation":"DeleteCategoryCommandHandler loads the category by command.CategoryId and throws NotFoundException (HTTP 404) when no row matches, so the delete never runs. Soft-deleted and cross-tenant rows are excluded by the default query filters.","triggerScenarios":"Send/DeleteCategory command with a CategoryId that does not exist, was already deleted, or belongs to another tenant.","commonSituations":"Deleting from a stale grid after another user removed the category; wrong tenant header; passing a brand/product ID by mistake.","solutions":["Confirm the CategoryId exists via the categories endpoint","Refresh the list and retry with a valid ID","Check tenant context if the category exists in another tenant"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"try { await mediator.Send(new GetCategoryByIdQuery(id)); return true; }\ncatch (NotFoundException) { return false; }","typeGuard":null,"tryCatchPattern":"try { await mediator.Send(new DeleteCategoryCommand(id)); }\ncatch (NotFoundException) { /* already deleted — no-op */ }","preventionTips":["Refresh tree data before deletes","Make deletes idempotent (swallow 404)","Verify tenant header on cross-environment testing"],"tags":["not-found","http-404","category","delete"],"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"}