rmi-backend/app/error_handlers.py

85 lines
2.6 KiB
Python

"""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.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:
# The core module may also register; we don't want to double-register.
# Skip if it already handled 422.
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
@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)