{"record":{"id":"84fa2a01cd9633fa","repo":"fullstackhero/dotnet-starter-kit","slug":"brand-command-brandid-not-found-restorebrandcommandhandler","errorCode":null,"errorMessage":"Brand {command.BrandId} not found.","messagePattern":"Brand (.+?) not found\\.","errorType":"exception","errorClass":"NotFoundException","httpStatus":404,"severity":"error","filePath":"src/Modules/Catalog/Modules.Catalog/Features/v1/Brands/RestoreBrand/RestoreBrandCommandHandler.cs","lineNumber":23,"sourceCode":"using Mediator;\nusing Microsoft.EntityFrameworkCore;\n\nnamespace FSH.Modules.Catalog.Features.v1.Brands.RestoreBrand;\n\npublic sealed class RestoreBrandCommandHandler(CatalogDbContext dbContext)\n    : ICommandHandler<RestoreBrandCommand, Guid>\n{\n    public async ValueTask<Guid> Handle(RestoreBrandCommand command, CancellationToken cancellationToken)\n    {\n        ArgumentNullException.ThrowIfNull(command);\n\n        // Disable only the SoftDelete filter so we can load a deleted row;\n        // tenant scoping stays in force, so cross-tenant restores cannot leak.\n        var brand = await dbContext.Brands\n            .IgnoreQueryFilters([QueryFilters.SoftDelete])\n            .FirstOrDefaultAsync(b => b.Id == command.BrandId, cancellationToken)\n            .ConfigureAwait(false)\n            ?? throw new NotFoundException($\"Brand {command.BrandId} not found.\");\n\n        brand.Restore();\n        await dbContext.SaveChangesAsync(cancellationToken).ConfigureAwait(false);\n        return brand.Id;\n    }\n}\n","sourceCodeStart":5,"sourceCodeEnd":30,"githubUrl":"https://github.com/fullstackhero/dotnet-starter-kit/blob/3f2959e683e9f83f13e55e1678c9119f63c7e8e5/src/Modules/Catalog/Modules.Catalog/Features/v1/Brands/RestoreBrand/RestoreBrandCommandHandler.cs#L5-L30","documentation":"RestoreBrandCommandHandler loads the brand with only the SoftDelete filter disabled, so only non-deleted rows (still tenant-scoped) are visible; it throws NotFoundException when nothing matches, mapped to HTTP 404. This means restoring an already-active brand, an unknown ID, or a foreign-tenant brand all produce this error — you cannot 'restore' something that is not currently soft-deleted.","triggerScenarios":"Send/RestoreBrand command with a BrandId that is not present as a soft-deleted row: already active, never existed, hard-deleted, or another tenant's brand.","commonSituations":"Double-invoking restore after the first call succeeded; guessing at IDs to recover data; pointing at a brand in the wrong tenant.","solutions":["Check whether the brand is already active via GET /brands/v1/{id} — if so, no restore is needed","Verify the BrandId is correct and belongs to the current tenant","If the row was hard-deleted, re-create the brand instead of restoring"],"exampleFix":"// before\nawait mediator.Send(new RestoreBrandCommand(id)); // may already be active\n// after\nvar existing = await mediator.Send(new GetBrandByIdQuery(id)); // 200 => nothing to restore\nif (existing is null) await mediator.Send(new RestoreBrandCommand(id));","handlingStrategy":"validation","validationCode":"bool isActive = await apiClient.GET<bool>($\"/brands/v1/{id}/exists\");\nif (isActive) return; // nothing to restore","typeGuard":null,"tryCatchPattern":"try { await mediator.Send(new RestoreBrandCommand(id)); }\ncatch (NotFoundException) { /* already active or never existed */ }","preventionTips":["Track soft-delete state client-side before calling restore","Make restore calls idempotent (ignore not-found as success)","Never restore across tenants"],"tags":["not-found","http-404","soft-delete","restore","catalog"],"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"}