best / backend /app /core /exceptions.py
anky2002's picture
feat: Add unit tests, production Docker, rate limiting, WebSocket notifications, data export (GDPR), error handling
56d23bb verified
Raw
History Blame Contribute Delete
3.01 kB
"""
Global exception handlers and error formatting.
"""
import logging
import traceback
from fastapi import FastAPI, Request, status
from fastapi.exceptions import RequestValidationError
from fastapi.responses import JSONResponse
from starlette.exceptions import HTTPException as StarletteHTTPException
from app.core.config import settings
logger = logging.getLogger(__name__)
def register_exception_handlers(app: FastAPI):
"""Register all global exception handlers."""
@app.exception_handler(StarletteHTTPException)
async def http_exception_handler(request: Request, exc: StarletteHTTPException):
"""Handle HTTP exceptions with consistent format."""
return JSONResponse(
status_code=exc.status_code,
content={
"error": True,
"status_code": exc.status_code,
"detail": exc.detail,
"path": str(request.url.path),
},
headers=getattr(exc, "headers", None),
)
@app.exception_handler(RequestValidationError)
async def validation_exception_handler(request: Request, exc: RequestValidationError):
"""Handle Pydantic validation errors with readable messages."""
errors = []
for error in exc.errors():
field = " → ".join(str(loc) for loc in error["loc"][1:]) # Skip 'body'
errors.append({
"field": field,
"message": error["msg"],
"type": error["type"],
})
return JSONResponse(
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
content={
"error": True,
"status_code": 422,
"detail": "Validation error",
"errors": errors,
"path": str(request.url.path),
},
)
@app.exception_handler(Exception)
async def general_exception_handler(request: Request, exc: Exception):
"""Handle unexpected exceptions."""
error_id = id(exc)
logger.error(
f"Unhandled exception [{error_id}]: {exc}\n"
f"Path: {request.url.path}\n"
f"{''.join(traceback.format_exception(type(exc), exc, exc.__traceback__))}"
)
# In production, don't leak error details
if settings.APP_ENV == "production":
detail = "An unexpected error occurred. Please try again later."
else:
detail = f"{type(exc).__name__}: {str(exc)}"
# Report to Sentry if configured
try:
import sentry_sdk
sentry_sdk.capture_exception(exc)
except ImportError:
pass
return JSONResponse(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
content={
"error": True,
"status_code": 500,
"detail": detail,
"error_id": str(error_id),
"path": str(request.url.path),
},
)