Add unified RequestValidationError handler to unify error response schema
- Add RequestValidationError handler that converts Pydantic validation errors to unified ErrorResponse format - Ensures all error responses return consistent schema: code, detail, metadata, correlation_id - Add field_errors count and first_field location to metadata for validation errors - Register handler in exception handler hierarchy before HTTPException handler - Add comprehensive tests for validation error responses - Update Backend-Development.md documentation to include correlation_id field and validation error details - All 44 error-related tests pass (38 existing + 6 new validation tests) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -632,14 +632,16 @@ Every non-2xx HTTP response body is a JSON object with this structure:
|
||||
"detail": "Jail 'example' not found",
|
||||
"metadata": {
|
||||
"jail_name": "example"
|
||||
}
|
||||
},
|
||||
"correlation_id": "550e8400-e29b-41d4-a716-446655440000"
|
||||
}
|
||||
```
|
||||
|
||||
**Fields:**
|
||||
- **`code`** (string, required): Machine-readable error code for client-side branching. Examples: `jail_not_found`, `rate_limit_exceeded`, `authentication_required`.
|
||||
- **`code`** (string, required): Machine-readable error code for client-side branching. Examples: `jail_not_found`, `rate_limit_exceeded`, `authentication_required`, `invalid_input`.
|
||||
- **`detail`** (string, required): Human-readable error message. Safe for displaying to users.
|
||||
- **`metadata`** (object, optional): Structured context data relevant to the error. Only includes data safe for client consumption (no sensitive internal state). Examples: offending parameter names, resource identifiers, time windows.
|
||||
- **`metadata`** (object, optional): Structured context data relevant to the error. Only includes data safe for client consumption (no sensitive internal state). Examples: offending parameter names, resource identifiers, field error counts, time windows.
|
||||
- **`correlation_id`** (string | null, optional): Unique request ID for tracing this error across logs and systems. Set by the `CorrelationIdMiddleware`. Use this to correlate client-side errors with server logs for debugging.
|
||||
|
||||
### Exception Hierarchy & Error Codes
|
||||
|
||||
@@ -655,6 +657,8 @@ All domain exceptions inherit from `DomainError` (defined in `backend/app/except
|
||||
| **401** | `AuthenticationError` | `authentication_required` | Authentication or authorization failure, invalid/expired credentials |
|
||||
| **429** | `RateLimitError` | `rate_limit_exceeded` | Rate limit exceeded, too many requests |
|
||||
|
||||
**Note on request validation errors:** Pydantic validation errors (from request body type mismatches, missing required fields, etc.) are automatically caught by the `_request_validation_error_handler` and converted to `ErrorResponse` with `code="invalid_input"`. The `metadata` field includes `field_errors` (count of validation failures) and `first_field` (location of the first error field) to help clients debug malformed requests.
|
||||
|
||||
### Implementing Error Handlers
|
||||
|
||||
Every exception category has a corresponding exception handler registered in `backend/app/main.py`. When a domain exception is raised:
|
||||
|
||||
Reference in New Issue
Block a user