# Story 2.17: Gestion d'Erreurs Graceful (Zero HTTP 500) Status: done ## Story En tant que **système**, je veux **ne jamais retourner d'erreurs HTTP 500 (Internal Server Error)**, de sorte que **les utilisateurs reçoivent toujours des messages d'erreur explicites et exploitables**. ## Acceptance Criteria 1. **Gestionnaire Global**: Toutes les exceptions non capturées doivent être interceptées par un gestionnaire global (Middleware). (NFR12) 2. **Format JSON Structuré**: Toutes les erreurs doivent retourner un corps JSON au format : `{error: "CODE", message: "...", details: {...}}`. (NFR19) 3. **Codes d'Erreur Standards**: Utiliser les codes d'erreur définis dans l'architecture : `INVALID_FORMAT`, `QUOTA_EXCEEDED`, `UNAUTHORIZED`, `FORBIDDEN`, `FILE_TOO_LARGE`, `PROVIDER_ERROR`, `INTERNAL_ERROR`. 4. **Masquage des Détails Techniques**: Les erreurs internes (véritables 500) doivent être logguées avec leur stack trace côté serveur, mais retournées au client comme `INTERNAL_ERROR` avec un message générique en français : "Une erreur inattendue s'est produite". 5. **Messages en Français**: Les messages d'erreur destinés à l'utilisateur final doivent être rédigés en français. (FR57) 6. **Zéro Stack Trace**: Aucune trace d'exécution (stack trace) ne doit être exposée dans les réponses API. (NFR12) ## Tasks / Subtasks - [x] **Task 1: Centralisation de la Gestion des Erreurs** (AC: #1, #2) - [x] 1.1 Créer `middleware/error_handler.py` (comme spécifié dans l'architecture) ou refactorer `middleware/security.py:ErrorHandlingMiddleware`. - [x] 1.2 Implémenter la capture des exceptions `HTTPException` de FastAPI pour uniformiser leur format. - [x] 1.3 Implémenter la capture de `Exception` (catch-all) pour transformer les erreurs imprévues en `INTERNAL_ERROR`. - [x] **Task 2: Standardisation des Exceptions Métier** (AC: #3) - [x] 2.1 Mettre à jour `utils/exceptions.py` pour que chaque exception possède un code d'erreur et un message par défaut. - [x] 2.2 Migrer les `exception_handlers` personnalisés de `main.py` (ValidationError, AllProvidersFailedError, etc.) vers le nouveau middleware ou les standardiser. - [x] **Task 3: Internationalisation des Messages** (AC: #5) - [x] 3.1 Définir une table de correspondance ou des constantes pour les messages d'erreur en français. - [x] 3.2 S'assurer que les erreurs de validation Pydantic sont également traduites ou formatées proprement. - [x] **Task 4: Tests de Robustesse** (AC: #6) - [x] 4.1 Test de format invalide (400 `INVALID_FORMAT`). - [x] 4.2 Test de dépassement de quota (429 `QUOTA_EXCEEDED`). - [x] 4.3 Test d'erreur provider (502 `PROVIDER_ERROR`). - [x] 4.4 Test d'erreur inattendue (ex: division par zéro forcée) pour vérifier la transformation en `INTERNAL_ERROR` (500). ## Dev Notes - **Composants touchés**: `middleware/error_handler.py`, `main.py`, `utils/exceptions.py` - **Pattern Architecture**: Suivre le format `{error, message, details?}` sans le champ `data` en cas d'erreur. - **Logging**: Utiliser `logger.exception()` pour les erreurs internes afin de garder la stack trace dans les logs (structlog) pour le Dashboard Admin. ### Project Structure Notes - Création de `middleware/error_handler.py` pour centraliser la logique. - Mise à jour de `main.py` avec des `app.exception_handler` pour intercepter les exceptions FastAPI/Starlette avant qu'elles ne soient converties en réponses par défaut. - Installation de `structlog` dans l'environnement de test pour assurer la compatibilité des logs structurés. ### References - [Source: _bmad-output/planning-artifacts/epics.md#Story 2.17] - [Source: _bmad-output/planning-artifacts/architecture.md#Error Handling] - [Source: main.py#exception_handlers] - [Source: utils/exceptions.py] ## Dev Agent Record ### Agent Model Used Gemini 2.0 Flash ### Implementation Plan 1. Création du middleware centralisé dans `middleware/error_handler.py`. 2. Refactorisation de `utils/exceptions.py` pour standardiser les exceptions métier. 3. Ajout des gestionnaires d'exceptions globaux dans `main.py`. 4. Mise à jour des tests existants impactés par le changement de format/code d'erreur. 5. Vérification finale avec la suite de tests complète (100% pass). ### Completion Notes List - ✅ Centralisation de la gestion des erreurs dans `middleware/error_handler.py`. - ✅ Standardisation du format JSON de réponse d'erreur : `{error, message, details}`. - ✅ Conversion automatique des erreurs de validation Pydantic (422) en 400 `INVALID_FORMAT`. - ✅ Masquage complet des stack traces client (0% HTTP 500 exposé). - ✅ Support de la propagation des headers (ex: `Retry-After`) dans les erreurs standardisées. - ✅ Correction des régressions dans les tests (signatures de mocks et messages attendus). **Corrections Code Review (2026-02-22) :** - ✅ `tests/test_error_handling.py` recréé (18 tests couvrant AC1–AC6) — fichier source supprimé par erreur. - ✅ `LanguageNotSupportedError.code` : `UNSUPPORTED_LANGUAGE` → `INVALID_FORMAT` (AC3). - ✅ `DocumentProcessingError.code` : `PROCESSING_ERROR` → `INTERNAL_ERROR` (AC3). - ✅ `/translate-batch` : format d'erreur structuré `{error, message, details}`, suppression de `str(e)` (AC2, AC4, AC6). - ✅ `/extract-texts`, `/reconstruct-document` : suppression exposition de `str(e)`, retour `INTERNAL_ERROR` structuré (AC4, AC6). - ✅ `handle_translation_error` : `details: {}` toujours présent même si vide (AC2). - ✅ `middleware/error_handler.py` : support `structlog` conditionnel, message fallback HTTPException en français (AC5). ### File List - `middleware/error_handler.py` (Nouveau) - `middleware/security.py` (Modifié) - `middleware/cleanup.py` (Modifié) - `middleware/__init__.py` (Modifié) - `middleware/validation.py` (Modifié) - `main.py` (Modifié) - `utils/exceptions.py` (Modifié) - `utils/__init__.py` (Modifié) - `utils/file_handler.py` (Modifié) - `routes/translate_routes.py` (Nouveau) - `tests/test_error_handling.py` (Nouveau — recréé lors de la code review) - `tests/test_admin_tier_change.py` (Modifié) - `tests/test_translation_log_1_8.py` (Modifié) - `tests/test_tier_rate_limit.py` (Modifié) - `tests/test_storage_tracker.py` (Modifié) - `tests/test_download_endpoint.py` (Modifié)