[High] Trail handlers return HTTP 400 instead of 404 for missing trails #57

Closed
opened 2026-05-05 04:15:06 +00:00 by spikerj · 1 comment
Owner

Severity: High (REST contract correctness)

Files:

  • SpikerSoft.Business/Domain/Map/Trails/Queries/GetTrailById/GetTrailByIdQueryHandler.cs (~24-38)
  • SpikerSoft.Business/Domain/Map/Trails/Commands/UpdateTrail/UpdateTrailCommandHandler.cs (~32-38)

Problem: Both handlers throw ArgumentException("Trail with ID {id} not found") when no row matches. TrailsController maps every ArgumentException to 400 Bad Request ("Invalid trail ID format"), so:

  • Clients can't distinguish malformed id (400) from missing trail (404)
  • The NotFound branch in the controller is effectively dead code
  • API consumers can't reliably retry vs handle missing resources

Fix:

  • Either return null (use TrailDto? and let the controller map null to 404)
  • Or throw a dedicated NotFoundException (or domain-specific equivalent) that the controller maps to 404
  • Reserve ArgumentException for genuinely invalid id format

Acceptance criteria:

  • GET /api/Trails/{id} with malformed id returns 400
  • GET /api/Trails/{id} with valid id format but no match returns 404
  • PUT /api/Trails/{id} for non-existent id returns 404
  • Unit tests for both branches
**Severity:** High (REST contract correctness) **Files:** - `SpikerSoft.Business/Domain/Map/Trails/Queries/GetTrailById/GetTrailByIdQueryHandler.cs` (~24-38) - `SpikerSoft.Business/Domain/Map/Trails/Commands/UpdateTrail/UpdateTrailCommandHandler.cs` (~32-38) **Problem:** Both handlers throw `ArgumentException("Trail with ID {id} not found")` when no row matches. `TrailsController` maps every `ArgumentException` to **400 Bad Request** ("Invalid trail ID format"), so: - Clients can't distinguish malformed id (400) from missing trail (404) - The `NotFound` branch in the controller is effectively dead code - API consumers can't reliably retry vs handle missing resources **Fix:** - Either return `null` (use `TrailDto?` and let the controller map null to 404) - Or throw a dedicated `NotFoundException` (or domain-specific equivalent) that the controller maps to 404 - Reserve `ArgumentException` for genuinely invalid id format **Acceptance criteria:** - [ ] GET /api/Trails/{id} with malformed id returns 400 - [ ] GET /api/Trails/{id} with valid id format but no match returns 404 - [ ] PUT /api/Trails/{id} for non-existent id returns 404 - [ ] Unit tests for both branches
Author
Owner

Resolved.

Introduced TrailNotFoundException (in SpikerSoft.Business/Domain/Map/Trails/Exceptions/). GetTrailByIdQueryHandler and UpdateTrailCommandHandler now throw it instead of ArgumentException when the row is missing. TrailsController catches TrailNotFoundException first and returns 404; ArgumentException continues to map to 400 for genuinely malformed ids.

Files changed:

  • SpikerSoft.Business/Domain/Map/Trails/Exceptions/TrailNotFoundException.cs (new)
  • SpikerSoft.Business/Domain/Map/Trails/Queries/GetTrailById/GetTrailByIdQueryHandler.cs
  • SpikerSoft.Business/Domain/Map/Trails/Commands/UpdateTrail/UpdateTrailCommandHandler.cs
  • SpikerSoft.Api/Domain/Map/Trails/TrailsController.cs
**Resolved.** Introduced `TrailNotFoundException` (in `SpikerSoft.Business/Domain/Map/Trails/Exceptions/`). `GetTrailByIdQueryHandler` and `UpdateTrailCommandHandler` now throw it instead of `ArgumentException` when the row is missing. `TrailsController` catches `TrailNotFoundException` first and returns 404; `ArgumentException` continues to map to 400 for genuinely malformed ids. Files changed: - `SpikerSoft.Business/Domain/Map/Trails/Exceptions/TrailNotFoundException.cs` (new) - `SpikerSoft.Business/Domain/Map/Trails/Queries/GetTrailById/GetTrailByIdQueryHandler.cs` - `SpikerSoft.Business/Domain/Map/Trails/Commands/UpdateTrail/UpdateTrailCommandHandler.cs` - `SpikerSoft.Api/Domain/Map/Trails/TrailsController.cs`
Sign in to join this conversation.