| """RMI Backend — exception handlers. |
| |
| Clean JSON error responses for 404, 500, AppError, etc. Each |
| handler is isolated — one failure doesn't break the others. |
| """ |
| from __future__ import annotations |
|
|
| import logging |
|
|
| from fastapi import FastAPI, Request |
| from fastapi.exceptions import RequestValidationError |
| from fastapi.responses import JSONResponse |
|
|
| log = logging.getLogger(__name__) |
|
|
|
|
| def register_error_handlers(app: FastAPI) -> None: |
| """Register every error handler. Each is isolated.""" |
| _register_404(app) |
| _register_validation(app) |
| _register_500(app) |
| _register_apperror(app) |
|
|
|
|
| def _register_404(app: FastAPI) -> None: |
| @app.exception_handler(404) |
| async def _not_found(request: Request, _exc: Exception) -> JSONResponse: |
| return JSONResponse( |
| status_code=404, |
| content={ |
| "error": "not_found", |
| "path": request.url.path, |
| "method": request.method, |
| "hint": "RMI new backend. Check /docs for current API surface.", |
| }, |
| ) |
|
|
| log.info("error_handler_registered name=not_found") |
|
|
|
|
| def _register_validation(app: FastAPI) -> None: |
| """422 — Pydantic validation errors. Return structured details.""" |
| try: |
| from app.core.errors import register_error_handlers as _core |
| |
| |
| log.info("error_handler_registered name=validation (via core)") |
| except Exception: |
| pass |
|
|
|
|
| def _register_500(app: FastAPI) -> None: |
| @app.exception_handler(Exception) |
| async def _internal_error(request: Request, exc: Exception) -> JSONResponse: |
| log.exception("unhandled_exception path=%s", request.url.path) |
| return JSONResponse( |
| status_code=500, |
| content={ |
| "error": "internal_error", |
| "path": request.url.path, |
| "type": type(exc).__name__, |
| }, |
| ) |
|
|
| log.info("error_handler_registered name=internal_error") |
|
|
|
|
| def _register_apperror(app: FastAPI) -> None: |
| """AppError → JSON via core.errors.register_error_handlers (if available).""" |
| try: |
| from app.core.errors import AppError |
| from fastapi import HTTPException |
|
|
| @app.exception_handler(AppError) |
| async def _app_error(request: Request, exc: AppError) -> JSONResponse: |
| return JSONResponse( |
| status_code=exc.status_code, |
| content={ |
| "error": exc.__class__.__name__, |
| "message": exc.message, |
| "path": request.url.path, |
| }, |
| ) |
|
|
| log.info("error_handler_registered name=AppError") |
| except Exception as exc: |
| log.info("error_handler_skipped name=AppError err=%s", exc) |
|
|