Fix exception handler overlap issue - add DomainError catch-all handler
**Problem:** Broad exception handlers created fragility where adding a new DomainError subclass without explicit registration would silently fall through to the generic exception handler, losing the specific error_code and metadata. **Solution:** 1. Import DomainError in main.py for explicit handler registration 2. Fix type hints in exception handlers from 'Exception' to specific types - NotFoundError handler now typed as 'NotFoundError' - BadRequestError handler now typed as 'BadRequestError' - ConflictError handler now typed as 'ConflictError' - DomainError handler now typed as 'DomainError' - ServiceUnavailableError handler now typed as 'ServiceUnavailableError' 3. Add DomainError as an explicit catch-all handler in the registration chain - Positioned after specific handlers, before HTTPException - Any unregistered DomainError subclass now gets correct error_code + metadata 4. Document the exception handler hierarchy with detailed comments 5. Update Backend-Development.md with handler hierarchy documentation 6. Update Architekture.md section 2.2 with exception handler details 7. Fix test expectations in test_main.py to verify ErrorResponse format **Impact:** Any new DomainError subclass now automatically gets correct HTTP 500 status, error_code, and metadata - even if developer forgets explicit handler. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -672,10 +672,35 @@ class MySpecificError(BadRequestError):
|
||||
4. **Implement `get_error_metadata()`** — Return only data safe for client consumption. Never leak internal state, file paths, or system details.
|
||||
5. **Raise from service code** — Never from repositories or utils. Exceptions represent business logic violations, not infrastructure errors.
|
||||
|
||||
### Exception Handler Hierarchy
|
||||
|
||||
All domain exceptions are automatically converted to `ErrorResponse` via handlers registered in `backend/app/main.py`. The handler registration order is critical:
|
||||
|
||||
```python
|
||||
# Handlers are registered from most specific to least specific:
|
||||
1. Network errors (Fail2BanConnectionError, etc.) → HTTP 502
|
||||
2. Auth/rate errors (AuthenticationError, RateLimitError) → HTTP 401/429
|
||||
3. Category handlers (NotFoundError, BadRequestError, ConflictError, etc.) → HTTP 404/400/409/500/503
|
||||
4. DomainError catch-all → HTTP 500 # ← Catches unregistered DomainError subclasses
|
||||
5. HTTPException (FastAPI built-ins) → HTTP varies
|
||||
6. ValueError (Pydantic validation) → HTTP 400
|
||||
7. Exception catch-all → HTTP 500 # ← Absolute last resort
|
||||
```
|
||||
|
||||
**Important:** The `DomainError` catch-all handler (step 4) is the safety net. If you add a new `DomainError` subclass without placing it in a category (e.g., `class MyError(DomainError)` instead of `class MyError(BadRequestError)`), it will still get the correct `error_code` and `metadata` via this handler instead of silently falling through to the generic exception handler.
|
||||
|
||||
**Critical caveat:** Every new `DomainError` subclass **must**:
|
||||
- Define an `error_code` class attribute (e.g., `error_code: str = "my_error"`)
|
||||
- Override `get_error_metadata()` if it needs to return context data
|
||||
- Inherit from the appropriate category (NotFoundError, BadRequestError, ConflictError, OperationError, or ServiceUnavailableError)
|
||||
|
||||
If you forget to implement `error_code` or `get_error_metadata()`, the fallback to parent class implementations will produce misleading error codes and empty metadata — check your tests!
|
||||
|
||||
**What NOT to do:**
|
||||
- ❌ Don't raise `HTTPException` from service code (bypass the ErrorResponse format).
|
||||
- ❌ Don't put sensitive information in `metadata` (database paths, SQL, internal IDs).
|
||||
- ❌ Don't derive error codes from class names using regex (fragile and non-self-documenting).
|
||||
- ❌ Don't create a `DomainError` subclass without a category (always inherit from one of the seven categories)
|
||||
|
||||
### Frontend Error Parsing
|
||||
|
||||
|
||||
Reference in New Issue
Block a user